今天开始读阮一峰的《ECMAScript 6 入门》,在这里记录下阅读过程中的要点,以便随时查阅。
let和const
顶层对象的属性与全局变量挂钩,被认为是js最大的败笔之一,ES6开始,全局变量将逐步与顶层对象脱钩。为了保持兼容性,一方面,var和function声明的全局变量,依旧是顶层对象的属性;另一方面,let,const,class命令声明的全局变量,不属于顶层对象的属性:
1
2
3
4var a = 1; window.a;//1 let b = 2; window.b;//undefined
const 声明一个只读常量,一旦声明就必须立即初始化,否则报错;声明之后不能修改,否则也会报错;
const实际上保证的不是变量的值不可改动,而是变量指向的那个内存地址不得改动。使用const声明引用类型,只能保证指针是固定的,但是指向的数据结构内的属性和方法是可变:
1
2
3
4const obj={}; obj.name = 'illidan'; obj.naem; // 'illidan'; obj = {}; //报错,改变了指针的指向
let和const声明的变量只在代码块内有效(代码块:大括号包起来的部分),在代码块外调用会报错
let和const没有变量提升,因此在它们声明之前调用变量会报错
1
2
3
4console.log(foo);//undefined var foo = 'haha' console.log(a); //reference error let a = 'hehe';
let和const不允许在相同作用域内,重复声明一个变量:
1
2
3
4
5
6
7
8
9
10
11
12function foo1(){ //报错 var a = 1; let a = 1; } function foo2(){ //报错 let a = 1; let a = 2; } function foo3(){//报错 let a= 1; const a = 2; }
因此不能在函数内部重新声明参数:
1
2
3
4
5
6
7
8function foo4(arg){ let arg;//报错 } function foo5(arg){ { let arg;//不报错 } }
for循环有个特别之处,循环语句是一个父作用域,循环体内是一个单独的子作用域
1
2
3
4
5for (let i = 0; i < 3; i++) { let i = 'abc'; console.log(i); } //3*abc
只要块级作用域内存在let或const,则它们声明的变量就绑定了这个区域,不再受外部的影响,这个区域称为暂时性死区(temporal dead zone),简称TDZ
1
2
3
4
5
6
7
8
9
10var temp = 123; if(true){ temp = 'abc';//reference error let temp; } var const = 1; if(true){ const=2;//reference error const const=3; }
TDZ也意味着typeof不再是一个百分百安全的操作
1
2
3typeof undeclared_variable //undefined typeof x;//reference error let x;
有些TDZ比较隐蔽:
1
2
3
4function bar(x=y,y=2){ return [x,y] } bar();//报错,参数x默认等于参数y,而此时y还没有声明
修改之后
1
2
3
4function bar(x=2,y=x){ return[x,y]; }; bar();//[2,2]
使用let或者const声明变量时,只有变量还在没有声明完成前使用,就会报错:
1
2
3var x = x;//undefined let x = x; // reference error const y = y; // reference error
总之,TDZ的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
为什么需要块级作用域?
ES5中只有全局作用域和函数作用域,没有块级作用域,会带来一些不合常理的场景:
第一种场景,内层变量可能会覆盖外层变量
1
2
3
4
5
6
7
8var temp = new Date(); function f(){ console.log(temp); if(false){ var temp = 'hello world'; //变量提升 } } f();//undefined
第二种场景,用来计数的循环变量泄露为全局变量:
1
2
3
4for(var i=0;i<10;i++){ console.log(i); }; console.log(i); //10
ES6引入块级作用域,明确允许在块级作用域中声明函数,行为类似于let,在块级作用域之外不可引用
1
2
3
4
5
6
7
8function f() { console.log('I am outside!'); } (function () { if (false) { // 重复声明一次函数f function f() { console.log('I am inside!'); } } f(); }());
上面的代码在ES5中运行,会得到'i an inside!'
1
2
3
4
5
6
7
8// ES5 环境 function f() { console.log('I am outside!'); } (function () { function f() { console.log('I am inside!'); } //函数声明提升 if (false) { } f(); }());
ES6理论上会得到'I am outside!',但是这样处理会对老代码产生很大的影响,为了兼容老代码,浏览器有自己的行为方式:
-允许在块级作用域内声明函数
-函数声明类似于var,即函数名提升到全局作用域或块级作用域头部,函数体不提升
-同时,函数声明还会提升到所在块级作用域的头部
因此,上面的代码在ES6浏览中实际执行的是:
1
2
3
4
5
6
7
8
9
10// 浏览器的 ES6 环境 function f() { console.log('I am outside!'); } (function () { var f = undefined; // 只提升变量名 if (false) { function f() { console.log('I am inside!'); } } f(); }()); //f is not a function;
要避免在块级作用域里声明函数,如果确实需要,则要写成函数表达式,而不是函数声明
转载于:https://www.cnblogs.com/azerothmemoir/p/6512453.html
最后
以上就是柔弱小刺猬最近收集整理的关于ES6学习笔记--let和const的全部内容,更多相关ES6学习笔记--let和const内容请搜索靠谱客的其他文章。
发表评论 取消回复