1、块级作用域绑定
1.1、var
在函数作用域或全局作用域通过var声明的变量,都会提升到作用域的顶部
1.2、let
块级作用域
- 函数内部
- 块中
暂时性死区TDZ
js引擎再扫描代码发现变量声明时,要么将他们提升到作用域顶部(var声明),要么放到TDZ中(let,const),访问TDZ中的变量会触发运行时错误,只有执行过变量声明语句,变量才会从TDZ中移出。
let
- 块级作用域
- 不会提升
- 禁止重复声明(会报错,var不会)
- 一旦执行到块外,会被销毁
let声明不会提升
1.3、const
- 声明必须初始化
-不会提升 - 不能重复声明
- 一旦执行到块外,会被销毁
- 不可以为const定义的常量再赋值=,
- 但如果常量是对象,可以修改对象的值,因为,保存的是对象的地址
1.4、循环中的作用域绑定
for(var i=0;i<10;i++)
{
process(items[i]);
}
console.log(i);//这里还可以访问i;
for(let i=0;i<10;i++)
{
process(items[i]);
}
console.log(i);//不可以访问i,将抛出错误
1.5、循环中的函数
var funcs=[];
for(var i=0;i<10;i++)
{
funcs.push(function(){
console.log(i);
})
}
funcs.forEach(function(func){
func();//输出10次数字10
})
循环里的每次迭代同时共享变量i,循环内部创建的函数全都保留了对相同变量的引用
1.6、循环中的let声明
var funcs=[];
for(let i=0;i<10;i++)
{
funcs.push(function(){
console.log(i);
})
}
funcs.forEach(function(func){
func();//输出1-9
})
每次循环的时候let声明都会创建一个新变量i,并将其初始化为i的当前值
let声明再循环内部的行为是标准中专门定义的,它不一定与let不提升特性相关
1.7、循环中的const声明
对于普通的for循环来说,可以在初始化变量的时候使用const,但是修改就会报错(如i++),但是运用在for-in和for-of循环中,不会修改已有绑定,而是创建一个新的绑定
1.8、全局作用域绑定
当var被用于全局作用域时,它会创建一个新的全局变量作为全局对象的属性,可能会覆盖一个已经存在的全局属性
但是如果在全局作用域使用let或者const,会在全局作用域下创建一个新的变量,但是不会被添加为全局对象的属性
const RegExp='Hi';
console.log(RegExp);//Hi
console.log(window.RegExp===RegExp);//false
如果希望在全局对象下定义变量,仍可以使用var,常见于在浏览器中跨frame或跨window访问
1.9、typeof
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'
引用类型数据,用typeof来判断的话,除了function会被识别出来之外,其余的都输出object
如果我们想要判断一个变量是否存在,可以使用typeof:(不能使用if(a), 若a未声明,则报错)
if(typeof a != 'undefined'){
//变量存在
}
变量不存在则报undefined
1.10、立即函数表达式
顾名思义,该表达式一被创建就立即执行。
是一个在定义时就会立即执行的 JavaScript 函数。
(function (x) {
console.log('x + x = ', x + x);
})(5) // x + x = 10
//后面小括号里面是参数
这是一个被称为 自执行匿名函数 的设计模式,主要包含两部分:
- 第一部分是包围在 圆括号运算符 () 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE
中的变量,而且又不会污染全局作用域。 - 第二部分再一次使用 () 创建了一个立即执行函数表达式,JavaScript 引擎到此将直接执行函数。
2、函数
js函数,无论在函数定义中声明了多少形参都可以传入任意数量的参数
2.1、es5中模拟默认参数
是模拟的
function fn(url,timeout,callback){
timeout=timeout||2000;
callback=callback||function(){}
}
timeout和callback为可选参数,不传入相应的参数会赋予一个默认值,在逻辑或表达式中,前一个数值为假值时,会返回后一个值,但是如果传入值是0.也会被认为是假的
2.2、es6中默认参数
可以在已指定默认值的参数后可以继续声明无默认值参数
function fn(url,timeout=2000,callback){
}
只有部位第二个参数传入值或主动为第二个参数传入undefined时才会使用timeout的默认值,对于默认参数值,null是一个合法的值。
2.3、默认参数值对arguments对象的影响
es5非严格模式下,命名参数的变化(在函数体内被重新赋值),变化会同步更新到arguments对象中,es5严格模式中则不会。
到了es6,表现和es5严格模式一样,arguments只由实际传进去的参数决定,不算默认参数
2.4、默认参数值的临时性死区
定义参数是会为每个参数创建一个新的标识符绑定,该绑定在初始化前不可引用,当调用函数的时候,会通过传入的值或参数的默认值初始化该参数。(let和const是在声明前放入TDZ)
函数参数有自己的作用域和临时死区,其与函数体的作用域是各自独立的,也就是说参数的默认值不可访问函数体内的声明的变量
2.5、处理无命名参数
js函数,无论在函数定义中声明了多少形参都可以传入任意数量的参数,这些就是无命名参数,在es5中,需要使用arguments时注意下标索引,在es6中引入了不定参数。
2.6、不定参数
在函数的命名参数前添加三个点就表明这是一个不定参数,该参数为一个数组,包含自它之后传入的所有参数,通过这个数组名即可足以访问里面的参数(而arguments对象包含的是实际传入的所有参数)
- 函数的length属性统计的是函数命名参数的数量,不定参数的加入不会影响length的值
- 每个函数最多只能声明一个不定参数,而且一定要放在所有参数的末尾
- 不定参数不能用于对象字面量setter之中,因为对象字面量setter的参数有且只能有一个
2.7、展开运算符
指定一个数组,将他们打散后作为各自独立的参数传入函数
Math.max()方法不允许传入数组
2.8、name属性
es6为所有函数新增了name属性
函数 函数名称
匿名函数表达式 被赋值的变量名称
//函数名字权重比被赋值的变量名字要高
getter函数 get 函数名字
setter函数 set 函数名字
bind()函数创建的函数 bound 函数名字
Function构造函数创建的函数 anonymous 函数名字
函数name属性的值不一定引用同名变量,它只是协助调试用的额外信息,所有不能使用name属性的值来获取对函数的引用
2.9、明确函数的多重用途
js函数有两个不同的内部方法:[[Call]]和[[Construct]],当通过new关键字调用函数时,执行的是[[Constuct]]函数,它负责创建一个新对象,然后再执行函数体,将this绑定在实例上,如果不通过new关键字调用函数,则执行[[Call]]函数,从而直接执行代码中的函数体。
具有[[Construct]]方法的函数都被称为构造函数,但是不是所有函数都有[[Construct]]方法,不是所有函数都可以用new调用,比如箭头函数
2.10、元属性 new.target
为了解决判断函数是否通过new关键字调用的问题,es6引入了new.target元属性,当调用函数的[[Construct]]方法时,new.target被赋值为new操作符的目标,也就是函数体内this的构造函数。如果调用[[Call]]方法,则new.target的值为undefined
2.11、块级函数
代码块中声明块级函数,在定义函数函数的代码块,块级函数会被提升到顶部。
二者的区别是,在该代码块中,块级函数会被提升至块的顶部,而let定义的函数表单式不会被提升
2.12、箭头函数
- 没有this,super,arguments和new.target绑定
- 不能通过new关键字调用
- 没有原型
- 不可以改变this绑定
- 不支持arguments对象,但是始终可以访问外围函数的arguments对象
- 不支持重复的命名参数
- 使用typeof和instaceof操作符调用箭头函数和调用其他函数没什么不一样
2.13、尾调用
尾调用指的是函数作为另一个函数的最后一条语句被调用
es6缩减了严格模式下尾调用栈的大小,如果满足以下的条件,尾调用不再创建新的栈帧,而是清除并重用当前的栈帧
- 尾调用不访问当前栈帧的变量(函数不是一个闭包)
- 在函数内部,尾调用式最后一条语句
- 尾调用的结果作为函数值返回
当写递归函数的时候,记得使用尾递归优化的特性,如果递归函数的计算量足够大,则尾递归优化可以大幅度提升程序的性能。
4、扩展对象的功能性
4.1、对象字面量语法扩展
4.1.1、属性初始值的简写
当一个对象的属性与本地变量同名时,不必在写冒号和值,写属性名就可以。因为当对象字面量里只有一个属性名称时,js引擎会在可访问作用域中查找同名变量然后赋值给属性
4.1.2、对象方法的简写
简写方法可以使用super关键字
4.1.3、可计算属性名
在对象字面量中使用方括号表示该属性名称是可计算的,内容将被求值,然后被转化成字符串
4.2、新增方法
4.2.1、object.is()
+0和-0在js中被表示为两个完全不同的实体,如果使用全等===,结果是两者相等。
NaN===NaN,结果是false。Object.is()的运行结果大部分情况和 ===运算符相等,唯一区别在于+0和-0被识别为不相等并且NaN和NaN等价
4.2.2、object.assign()
接受任意数量的元对象,并按指定的顺序将属性复制到接受对象中,如果多个源对象具有同名属性,则排位靠后的源对象会覆盖排位靠前的。
不能将提供者的访问器属性复制到接收对象中
4.3、重复的对象字面量属性
es6移除了重复属性检查,不会再报错,取最后一次赋值
4.4、自有属性枚举顺序
数值键在前,字符串键在后,数值升序,字符串按插入的顺序
4.5、增强对象的原型
4.5.1、改变对象的原型
es5中添加了object.getPrototypeOf()来返回任意指定对象的原型
es6中添加了object.setPrototypeOf()来改变任意指定对象的原型,接受两个参数:被改变原型的对象以及替代第一个参数原型的对象。
对象原型的真实值被存储在内部专用属性[[Prototype]]中,调用object.getPrototypeOf()来返回存储在其中的值,调用object.setPrototypeOf()来改变其中的值。