不得不吐槽一下找工作真的是一件费时费力费精神的活儿,如今总算找到一个自己比较满意的工作,终于可以捡起前面落下的ES6开始学习,啊,反观张鑫旭等大神没几天就更新一次的blog,再看看自己的,路漫漫其修远兮呐~~~
let
1 .循环体里的let
结合下面代码,在for循环中let命名的变量i只在本次循环中有效,因此每一次循环i变量实际上都是一个新的变量。因此最后输出的结果是6,如果使用var进行变量命名,输出结果为1。
var a=[];
for(let i =0;i<=6;i++){
a[i] = function(){
console.log(i)
}
}
for循环有一个特别之处,循环语句部分是一个父作用域,但循环体内是一个单独的子作用域。如下代码:
for(let i = 0;i<3;i++){
let i='abc';
console.log(i);
} //输出结果为abc(3次)
2不存在变量提升
使用var进行变量命名会有变量提升的现象,即如果变量在声明之前使用,会报undefined,但let进行变量明明不会有这种现象存在。关于变量提升,可详细参考《你不知道的javascript(上卷)》
3暂时性死区
暂时性死区的意思是只要块级作用域里存在let命令,它所声明的变量就会绑定这个区域,不会受到外部的影响。如下代码
var a='213';
if(true){
a='abc';//referencrError
leta;
}
“暂时性死区”也意味着typeof不再是一个百分之百安全的操作,如果在let命名之前使用,会报错。但是,如果一个变量没被命名,使用typeof函数会报undefined。ES6规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
4不允许重复声明
和var不同,Let不允许在同一个作用域内重复声明同一个变量,而var遇到没有值的重复的变量声明会忽略掉。
5.有关块级作用域
let实际上为 JavaScript 新增了块级作用域。块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了。
函数能不能在块级作用域之中声明,是一个相当令人混淆的问题。ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。因此下面两种声明方式在ES5严格模式里面是非法的
If(true){
function f(){}
}
try{
function f(){}
}catch(e){
}
而在ES6里面引入了块级作用域,是可以在里面进行函数声明的。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
为了减轻因此产生的不兼容问题,ES6在附录B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。
a.允许在块级作用域内声明函数。
b.函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
c.同时,函数声明还会提升到所在的块级作用域的头部。
上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。
根据这三条规则,在浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var声明的变量。如下
function test(){console.log('I amoutside');}
(function(){
if(false){
functiontest(){
console.log('Iam inside');
}
}
test();
})() //报错,test is not a function
考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
const
const声明一个只读的常量。一旦声明,常量的值就不能改变。如下
const p = 3.14;
p = 1234;// Uncaught TypeError
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX isnot defined
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo"is read-only
顶层对象的属性与全局变量挂钩,被认为是JavaScript语言最大的设计败笔之一。这样的设计带来了几个很大的问题,首先是没法在编译时就报出变量未声明的错误,只有运行时才能知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的);其次,程序员很容易不知不觉地就创建了全局变量(比如打字出错);最后,顶层对象的属性是到处可以读写的,这非常不利于模块化编程。另一方面,window对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合适的。ES6为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从ES6开始,全局变量将逐步与顶层对象的属性脱钩。
以上这些就是ES6有关于let和const使用和注意点的一些总结,明天继续下一章节的学习。