# JavaScript 基础
# 历史背景
1994年Netscape公司成立,并推出了自己的浏览器的免费版本 Netscape Navigator,很快就占有了浏览器市场。 1995年,微软公司开始加入,并很快发布了自己的 Internet Explorer 1.0。
1995年SUN开发了Java技术,这是第一个通用软件平台。Java拥有跨平台、面向对象、泛型编程的特性,广泛应用于企业级Web应用开发和移动应用开发。Java也伴随着互联网的迅猛发展而发展,逐渐成为重要的网络编程语言。名噪一时。
同年,当时在Netscape就职的Brendan Eich(布兰登·艾奇),正为Netscape Navigator 2.0浏览器开发的一门名为LiveScript
的脚本语言,后来Netscape与Sun Microsystems组成的开发联盟,为了让这门语言搭上Java这个编程语言热词,将其临时改名为JavaScript
,日后这成为大众对这门语言有诸多误解的原因之一。
JavaScript最初受Java启发而开始设计的,目的之一就是“看上去像Java”,因此语法上有类似之处,一些名称和命名规范也借自Java。但JavaScript的主要设计原则源自Self和Scheme。JavaScript与Java名称上的近似,是当时Netscape为了营销考虑与SUN达成协议的结果。
所以,JavaScript和Java其实没有半毛钱关系
JavaScript推出后在浏览器上大获成功,微软在不久后就为Internet Explorer 3.0浏览器推出了JScript
,以与处于市场领导地位的Netscape产品同台竞争。JScript也是一种JavaScript实现,这两个
JavaScript语言版本在浏览器端共存意味着语言标准化的缺失,对这门语言进行标准化被提上了日程。
在1997年,由Netscape、SUN、微软、宝蓝等公司组织及个人组成的技术委员会在ECMA(欧洲计算机制造商协会)确定定义了一种名叫ECMAScript
的新脚本语言标准
,规范名为ECMA-262
。
JavaScript成为了ECMAScript的实现之一。ECMA-262 第五版,即是ES5。
ECMA-262,包括ES5, ES6等是一个标准,JavaScript是ECMAScript的一个实现
# 浏览器大战
在网景导航2.0和IE 3.0出现之后的几年间,网景和微软公司不停的发布新版本的浏览器,支持更多的新功能。自此拉开了浏览器之战的序幕。
1994 Netscape Navigator
1995 Internet Explorer
2004 Firefox 1.0
2006 Internet Explorer7.0
2006 Firefox 2.0
2008 Chrome released
浏览器大战比的是什么?
视觉体验(渲染排版
)和速度(脚本运行
)
一个完整的浏览器包括:
- 排版引擎(内核)
- JS引擎
# 排版引擎 Layout engine/rendering engine
# JS引擎
常见浏览器引擎
- Chrome: webkit + V8
- Safari:
- Firefox:
- IE:
- Edge:
# 简史
JavaScript最早的出现是为了解决当网速较慢,输入的信息需要在后台服务器处理验证后返回较慢,造成用户体验很差。需要一个客户端的脚本语言处理,能直接在前端处理简单的用户验证。
JavaScript最早由网景公司(netscape)开发,早期开发的版本为JavaScript 1.0.
JavaScript的出现带来巨大的成功,于是微软也参与其中,开发了自家的JS版本,命名为JScript。
之后在1997年,ECMA-欧洲计算机制造商协会,定义了ECMAScript
的标准。自此以后,浏览器开发场商开始致力于将ECMAScript
作为各自JavaScript实现的基础,也在不同程度上取得了成功。
# JavaScript实现
一个完整的JavaScript的实现应该由下列三个不同的部分组成 核心: ECMAScript 提供语言功能 文档对象模型 : DOM 提供访问和操作网页内容的方法和接口 浏览器对象模型: BOM 提供与浏览器交互的方法和接口
JavaScript的这三个组成部分在当前的五个主要的浏览器中分别得到不同程度的支持,
对ECMAScript的支持度越来越高
对DOM的支持相差很多
对BOM的支持也相差很多
# ECMAScript
由ECMA-262定义的JavaScript,与浏览器没有依赖关系
。实际上这门语言本身并不包含输入
和输出
定义。Web浏览器只是的宿主环境
之一。宿主环境不仅提供基本的语言实现,同时也会提供语言的扩展,以便语言与环境之间实现交互对接。
ECMAScript到底定义了什么?
- 语法
- 类型
- 语句
- 关键字
- 保留字
- 操作符
- 对象
ECMAScript的版本 ECMAScript的不同版本称为不同的版次,以第x版表示。ECMA-262的版本历史如下
- ECMAScript 5.0(ES5) 2009
- ECMAScript 6.0(ES6 ES2015) 2015.06
- ECMAScript 7.0(ES7 ES2016) 2016
所以事实上,ECMAScript是从核心(语言)上定义了基础。
# DOM (Document Object Model)
DOM-文档对象模型是针对XML,但经过扩展用于HTML的应用程序编程接口。DOM到把整个页面映射为一个多层节点结构。通过DOM创建的这个表示文档的树形图,开发人员获得了控制页面内容的和结构的主动权,借助DOM提供的API,开发人员可以轻松自由的删除,添加替换或修改任何节点。
在IE4和Netscape Navigator4,分别支持的不同形式的DHTML(Dynamic HTML)上。在此基础上,开发人员,首次无需重新加载网页就可以修改其外观和内容了。 然而,DHTML给Web技术发展带来巨大进步的同时也带来了巨大的问题,由于Netscape和微软在开发DHTML方面各抒己见,过去只编写一个HTML
页面就能在任何浏览器中运行的时代结束了。 所以,此时,又需要一个标准了。
于是,W3C(World Wide Web Consortium,万维网联盟)开始着手规划DOM
DOM定义了什么
- DOM Core
- DOM HTML
DOM的级别
- DOM Level1 1998
- DOM Level2
- DOM Level3
Note
DOM并不只是针对Javascript,很多别的语言也实现了DOM。不过,在Web浏览器中,基于ECMAScript实现的DOM的确已经成为JavaScript这门语言的一个重要组成部分
# BOM (Browser Object Model)
使用BOM可以控制浏览器显示界面以外的部分,但问题是BOM没有统一的标准。在HTML5中得到了解决。
# 基本概念
# 语法
1.变量名区分大小写 2.标识符
首字母仅能为字母 下划线 美元符
其他字母可以为 字母 下划线 美元符号 或者数字
标识符不能为关键字
标识符的命名推荐使用驼峰式(camelCase)写法,ECMAScript 标准 camelCase 命名法的意思是 首字母采用小写,其他字母大写 eg: getNameById snake 命名法采用 小写字母加下划线的方式 eg: get_name_by_id
3.严格模式
ECMAScript 5 新引入的概念
'use script'
or "use script"
严格模式为JavaScript定义了一种不同的解析与执行模型
使用严格模式的目的:
- 消除JavaScript语法的一些不合理,不严谨之处,减少一些怪异的行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 提高编译的效率,增加运行速度
- 为未来的新版本提供铺垫
4.关键字和保留字 关键字和保留字不能作为标识符 关键字用于表示控制语句的开始或结束 ,或者执行特定操作等 保留字没什么特定用途,但是未来可能作为关键字,所以保留
5.变量
ECMAscript的变量是松散类型,该变量可以用来保存任何类型的数据。初始化变量使用var
关键字
var number;
如果在定义变量时不初始化值,那么默认值为undefined
6.JavaScript中存在变量的作用域的问题
function test(){
var message = “hi”;
}
test();
alert(message);
//上述代码在运行的过程中出现问题:
//因为message是局部变量,所以在执行完函数后就会被销毁
若要定义全局变量可以不使用
var关键字,即:省略var关键字会创建全局变量。但是不推荐这样使用
在 function test(){
message = “hi”;
}
test();
alert(message);
7.数据类型(7种 = 6+1) ECMAScript中有5种简单的数据类型(基本数据类型)
undefined,null,Boolean,Number,String, BigInt
和一种复杂数据类型object
,object本质上是由一组无序键值对组成的
8.如何确定一个变量的数据类型?
使用typeof
操作符
可能的返回结果
- "undefined" 值未定义
- "boolean" 布尔值
- "string" 字符串
- "number" 数值
- "object" null或对象
- "function" 函数
eg:
var message = 'some string';
console.log(typeof message ); //string
console.log(typeof 9); //number
Note:有时在使用typeof
时会返回一些比较奇怪的值比如,返回一个object
或是function
typeof null;//object
typeof functionName; //function
9.从技术的角度考虑,函数在ECMAScript中是对象,不是一种数据类型。但是使用typeof测试一个函数名时返回的确实是function
,但是在数据类型中是不存在function数据类型的,这点比较奇怪。
根据js高级编程的说法,是因为函数存在一些特殊的属性和值,所以有必要和object做一区分。
# 数据类型
1.undefined 类型 任意未被初始化的变量的值都为undefined
2.null 类型 本质为空对象指针。typeof null 返回一个object可以验证这一点 如果希望一个变量保存一个对象而尚未保存时,最好初始化为null,以和undefined区分
null和undefined 的关系
undefined
值是派生自null
的所以
undefined == null; true
但是使用严格等于 ===
undefined === null; false
3.Boolean 类型
Boolean类型仅有两个值 true
和 false
note:在ECMAScript中将TRUE
和 FALSE
看作是标识符,同时在ECAMScript中将TRUE
和 FALSE
看作是标识符
可以显示调用转型函数Boolean()
进行转型,转型规则详见书P26
eg:
var message = "hello world";
var messageAsBoolean = Boolean(message);
但是存在隐式转化,需要分清楚到底什么情况下该用隐式转化
var message = "hello world";
if(message){
alert("value is true");
}
4.Number类型 数值字面量
基本的是 10进制 八进制 前面加一个 0 (严格模式下 八进制数无效) 十六进制 前面加0x
浮点数值
由于保存浮点数的内存是保存整数的两倍,所以ECMAScript可能会将浮点数值转换成为整数值
note:永远不要测试某个特定的浮点数值
,因为浮点数计算时存在舍入误差
数值范围
ECMAScript保存的数值范围在Number.MAX_VALUE
和Number.MIN_VALUE
之间
如果超出其值会变为特殊的infinity值正无穷infinity
,负无穷-infinity
NaN值
Not a Number
这个数值用来表示一个本来要返回数值的操作数未返回数值的情况
特点:
A.任何涉及NaN的操作都会返回NaN
B.NaN与任何值都不相等,包括NaN本身
isNaN() 用于判断一个值是不是 不是Number类型 , 运行原理: 这个函数在接受一个参数后,会尝试将这个参数转换为一个数值,如果可以转换那么证明这个参数是数值类型返回false,不能转换,返回true;
eg:
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false
alert(isNaN("10")); //false 可以被转化为数值10
alert(isNaN("blue")); //true
alert(isNaN(true)); //false 可以被转化为数值1
alert(isNaN({})); //false
数值转换 存在三个数值转换的函数 Number() parseInt() parseFloat() Number() 函数适用于所有的数据类型 而parseInt() parseFloat() 适用于将一个字符串转换为数值 Number()函数的转换规则 详见书P30
parseInt()函数转换字符串时规则: A.如果第一个是空格,会忽略直到找到第一个非空格字符 B.如果第一个字符不是数字字符或者负数,会返回NaN C.如果第一个字符是数字字符,那么会继续解析第二个字符,直到解析了所有后续字符或者遇到一个非数字字符
note:parseInt() 能够识别出各种整数格式(十进制,八进制,十六进制等等)
note:parseInt(string, radix); 能够指定第二个参数为解析转换的进制基数,不指定基数意味着让parseInt()
决定如何解析输入的字符串,为了避免错误的解析,建议使用时加上第二个参数。
parseFloat()只能转换10进制的数
5.String类型
' '
与" "
在使用上是一样的。
特点:长度不可变
字符串一旦创建,他们的值就不能再改变了,要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串去填充这个字符串
如何转换为一个字符串?
方法A:使用每个值基本都有的toString()方法,该方法返回相应值的字符串的表现
数值
,布尔值
,对象
,字符串
每个字符串都有一个toString()的方法,但是null
和undefined
没有这个方法
note: toString()方法在使用时还可以传入一个参数radix,用于指定输出时的基数
方法B:如果没有办法确定其值是不是null或是undefined,那么可以使用转型函数String(): 处理规则:
如果值有toString()方法则调用该方法 ,并返回相应的结果
如果值是null,则返回"null"
如果值是undefined,则返回"undefined"
连接字符串?
使用+
运算符
6.Object类型
在ECMAScript中,Object类型是所有它的实例的基础
即:Object类型所具有的任何属性和方法也同样存在于更具体的对象中
Object 的每个实例都具有下列方法和属性:
- A.constructor :保存着用于创建当前对象的函数 eg: var o = new Object() ; //constructor 就是Object
- B.hasOwnProperty(propertyName) : 用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在
- C.isPrototypeOf(object) : 用于检查传入的对象 是否是传入对象的原型
- D.propertyIsEnumerable(propertyName): 用于检查给定的属性是否能够使用for-in 语句来枚举
- E.toLocaleString() 返回对象的字符串表示,该字符串与执行环境的地区对应
- G.toString():返回对象的字符串表示
- H.valueOf(): 返回对象的字符串,数值或者布尔值表示。 通常与toString()的返回值相同
var obj = {};
{}
__proto__:
constructor:ƒ Object()
hasOwnProperty:ƒ hasOwnProperty()
isPrototypeOf:ƒ isPrototypeOf()
propertyIsEnumerable:ƒ propertyIsEnumerable()
toLocaleString:ƒ toLocaleString()
toString:ƒ toString()
valueOf:ƒ valueOf()
__defineGetter__:ƒ __defineGetter__()
__defineSetter__:ƒ __defineSetter__()
__lookupGetter__:ƒ __lookupGetter__()
__lookupSetter__:ƒ __lookupSetter__()
get __proto__:ƒ __proto__()
set __proto__:ƒ __proto__()
note:ECMAscript中定义的对象的行为并不一定适用于Javascript中的其他对象,比如浏览器环境中的对象,BOM和DOM中的对象都属于宿主环境。
# 操作符
1.相等操作符 在Javascript中确定两个变量是否想等有两种方式: 方式一: 采用 == 在比较时先对比较的对象转化为相似类型的对象 //先转化再比较,转化规则详见P51 方式二: 采用 === 比较时不转化直接比较
# 语句
1.for-in语句 for-in 语句是一种精准的迭代语句,可以枚举对象的属性
eg:
for (var x in y){
//code here
}
缺陷:因为ECMAScript中的属性是没有顺序的,所以由for-in 循环输出的属性的顺序是不确定的
2.switch语句在比较时使用的是全等操作符
# 函数
1.理解函数的参数
- A.ECMAScript 函数的参数与大多数语言中函数的参数略有不同,不进行参数类型和数量的验证 反映出ECMAScript 函数参数的特点是:函数参数仅提供便利,但不是必须的
- B.可以在函数的内部使用arguments[]的方法取到传入的参数,因为函数的参数在内部使用数组来维护的
- C.可以利用arguments[]的方法根据传入的参数和数量分别适配不同的功能以模拟重载的功能
- D.arguments对象可以与命名的参数一起使用,在非严格模式下可以对其进行更改,且二者的数值保持同步
function add (arge){
arguments[0] = arge + 10;
}
- E.没有传递值的参数默认值为undifined
- F.ECMAScript中所有参数传递的都是值,不可能通过引用传递
2.没有重载 在ECMAScript中因为参数是由数组维护实现的,不存在函数签名(接受的函数参数的类型和数量)的验证特性所以不存在重载; 如果同时定义了两个同名函数,那么后定义的函数为真正的实现体; 可通过对arguments的控制实现伪重载;
# 引用类型
引用类型的值(对象)是引用类型的一个实例。 在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它通常也被称为类。 note:因为ECMAScript并不是一门面向对象的语言,所以这里的类的说法不大正确,准确应该时对象定义,描述一类对象所具有的属性和方法。
Object类型 对象,是某个特定引用类型的实例
。创建Object类型的示例方法有:
1. new + 构造函数
var person = new Object();
person.name = "yahoo";
2.字面常量法
var person = { name : "yahoo" }
3.访问一个对象属性的方法 A.点访问法 对象.属性
eg:
person.name
B.下标访问法 对象[]
eg:
person["name"];
or
var proper = "name";
person[proper];
区别:点访问法更加的直观
但是下标访问法可以使用变量
作为参数,所以更加灵活。
4.Array类型 定义数组的两种方式 A.使用Array
eg: var colors = new Array(x);
B.使用 []
eg: var colors = ['','',''];
数组的length属性是可以动态更改的(不是只读的)
eg: colors.length = 5;
可以利用这一特性 :向数组的最后插入新值
eg: colors[colors.length] = xx;
如何检测数组 方法1:使用instanceof 方法
eg: value instanceof Array
缺陷:使用instanceof操作符的问题在于,它假定只有一个全局执行环境,如果网页中包含多个框架,那么实际上就存在两个以上版本的的Array构造函数。 所以推荐使用下面的方法 方法2
//某些浏览器 可能不支持此方法
Array.isArray(value)
数值字符串转换
- 数组转化为字符串:使用toString()方法
# Function类型
在ECMAScript中函数实际上就是对象
每个函数就是Function类型的实例,而且与其他引用类型一样具有属性和方法 由于函数是对象,因此函数名实际上就是一个指向函数对象的指针,不会与某个函数绑定
函数的定义方式有三种
- A. 函数声明 function 函数名(){}
- B. 函数表达式 var 变量名 = function (){}; note: 此种方式定义时没有函数名并且有最后一个封号,就像定义变量是一样的
- C.使用Function 构造函数,但是不推荐使用
ECMAScript中函数不存在重载
函数声明与函数表达式 解析器在向执行环境中加载数据时,对函数声明和函数表达式的处理不同。
- 解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);
- 对于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行
在代码执行前,解析器通过一个名为函数声明提升
的过程,读取并将函数声明添加到执行环境中去。
函数内部属性 A.arguments 一个类数组的对象,里面保存着传入函数的参数。 note:该对象有一个callee 的属性,该属性是一个指针,指向拥有arguments对象的函数 该属性可以解除函数名在递归时的耦合问题
eg:
var sum = function (n){
if(n < 1 ){
return 1;
}else{
return n * arguments.callee(n-1);
}}
B.this 对象
this对象引用的是函数据以执行的环境对象,也就是this
的值
函数的属性和方法 因为函数是对象,所以也存在属性和方法
每个函数包含两个属性length
和prototype
- length 表示希望接收的命名参数的个数
- prototype 对于ECMAScript中的引用类型而言,
prototype
是保存它们所有的实例方法的真正所在
eg:toString(),valueOf()等方法实际上都保存在prototype名下,只不过通过各自对象的实例方法访问罢了
每个函数包含非继承
而来的方法apply()
,call()
,bind()
这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内的this对象的值
# 基本包装类型
为了便于操作基本类型,ECMAScript还提供了3个特殊的引用类型:包括Boolean
,Number
,String
实际上,每当读取一个基本类型值的时候,后台就会自动创建一个对应的基本包装类型的对象从而让我们能够调用一些方法来操作这些数据
引用类型和基本包装类型的主要区别就是对象生存期。 使用new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型在一行代码的执行瞬间,然后立即销毁。这意味着我们不能在运行时为基本包装类型添加属性和方法。
当然可以显示的创建基本包装类型,但是并不推荐这么做;
对于基本类型而言,使用typeof返回的时基本类型名 ,但是对于基本的包装类型而言使用typeof返回的是object
# 单体内置对象
对于内置对象的定义为:由ECAMScript实现提供的,不依赖于宿主环境,这些对象在ECMAScript程序执行之前都已经存在了
eg: Object Array String Global Math
Global对象 Global对象是ECMAScript中最特别的一个对象 首先,从任何角度看,这个对象是不存在的; 其次,不属于任何其他对象的属性和方法都是它的属性和方法
note:虽然在ECMAScript中并没有指出如何直接访问Global对象,但Web浏览器都是将这个全局对象作为window对象的一部分加以实现的
即:在全局作用域中声明的所有变量和函数,都成为window对象的属性
- URI编码
- eval方法
- Global对象的属性
- window对象
在全局作用域中声明的所有的变量和函数,就都成为了window对象的属性
Math对象 max(),min(): 求一个数组中的最大和最小项 但是:
eg:
var numbers = [1,2,3,4,5,6];
var Max = Math.max(numbers);
console.log(Max); // 结果是NaN
解决方法:
var numbers = [1,2,3,4,5,6];
var Max = Math.max.apply(Math,numbers);
console.log(Max); //结果为 6 正确
note:这个技巧关键把Math对象作为apply()的第一个参数,从而正确的设置this 的值
ceil() 向上舍入 floor() 向下舍入 round() 标准四舍五入
random() 该方法返回一个大于等于0小于1的一个随机数 实现任意范围的随机数生成:
var number = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值) eg:生成1到10 的随机数 var number = Math.floor(Math.random() * 10 + 1);
function getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
# 属性描述符
在ES5之前,JavaScript语言本身并没有提供可以直接检测属性特性的方法,比如判断属性是否是只读。但是在ES5开始,所有的属性都具备了属性描述符
# 属性值
- writeable(可写): 如果设置为false,那么该值不可被修改
- enumerable(可枚举): 属性是否可以出现在属性枚举中,eg: for in
- configurable(可配置): 是否可以修改属性描述符的值
可以通过使用Object.defineProperty来添加一个属性或者修改一个已知的属性