学习笔记。
let 和 const
现象:var 声明的变量存在变量提升的特性
为了加强对变量生命周期的控制,ECMAScript 6 引入了块级作用域。
块级作用域存在于:
- 函数内部
- 块中(字符 { 和 } 之间的区域)
块级声明用于声明在指定块的作用域之外无法访问的变量。
let 和 const 都是块级声明的一种。特性有:
- 不会被提升
- 重复声明报错
- 不绑定全局作用域
const 用于声明常量,其值一旦被设定不能再被修改,否则会报错。
值得一提的是:const 声明不允许修改绑定,但允许修改值。
临时死区(Temporal Dead Zone),简写为 TDZ。let 和 const 声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,会导致报错。
这是因为 JavaScript 引擎在扫描代码发现变量声明时,要么将它们提升到作用域顶部(遇到 var 声明),要么将声明放在 TDZ 中(遇到 let 和 const 声明)。访问 TDZ 中的变量会触发运行时错误。只有执行过变量声明语句后,变量才会从 TDZ 中移出,然后方可访问。
循环中的块级作用域
直接看例子
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function () {
console.log(i);
};
}
funcs[0](); // 0
如果将let换成var,结果都是3,这个大家都知道,解决办法也都知道,使用立即执行匿名函数创建函数级作用域做中转,可是在这里竟然天然就是对的,这里面是不是有点道道呢。
简单的来说,就是在 for (let i = 0; i < 3; i++) 中,即圆括号之内建立一个隐藏的作用域。其实你也可以理解为 let 声明模仿了闭包的做法来简化循环过程。因为在 babel 中,确实也是如此处理的。
还有一个区别的现象就是:使用for循环中,使用const会报错,因为常量不能被重新赋值,但是在for-in循环中确实正常的。
在 for in 循环中,每次迭代不会修改已有的绑定,而是会创建一个新的绑定。
最佳实践
日益普及:默认使用 const,只有当确实需要改变变量的值的时候才使用 let。这是因为大部分的变量的值在初始化后不应再改变,而预料之外的变量之的改变是很多 bug 的源头。
箭头函数
箭头函数和普通函数区别
- 没有this
- 箭头函数没有 this,所以需要通过查找作用域链来确定 this 的值。
- 箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this
- 因为箭头函数没有 this,所以也不能用 call()、apply()、bind() 这些方法改变 this 的指向
- 没有 arguments
- 可以访问外围函数的 arguments 对象
- 果我们就是要访问箭头函数的参数呢?你可以通过命名参数或者 rest 参数的形式访问参数
- 不能通过 new 关键字调用
- JavaScript 函数有两个内部方法:[[Call]] 和 [[Construct]]。
- 当通过 new 调用函数时,执行 [[Construct]] 方法,创建一个实例对象,然后再执行函数体,将 this 绑定到实例上。
- 当直接调用的时候,执行 [[Call]] 方法,直接执行函数体。
- 没有 new.target,因为不能使用 new 调用,所以也没有 new.target 值
- 没有原型,由于不能使用 new 调用箭头函数,所以也没有构建原型的需求,于是箭头函数也不存在 prototype 这个属性。
- 没有 super,连原型都没有,自然也不能通过 super 来访问原型的属性,所以箭头函数也是没有 super 的,不过跟 this、arguments、new.target 一样,这些值由外围最近一层非箭头函数决定。
MDN介绍:箭头函数表达式的语法比函数表达式更短,并且不绑定自己的this,arguments,super或 new.target。这些函数表达式最适合用于非方法函数(non-method functions),并且它们不能用作构造函数。
那么什么是 non-method functions 呢?
对象属性中的函数就被称之为 method,那么 non-mehtod 就是指不被用作对象属性中的函数了