理解ES6: 块作用域
2017-06-29 19:32
267 查看
这是Nicholas Zakas的新作,原文链接:https://github.com/nzakas/understandinges6/blob/master/manuscript/01-Block-Bindings.md
var声明与变量提升现象
这是前ES6时期var的问题,变量会被JS引擎处理成好像它们的声明被放在函数作用域(或者全局作用域)的顶端。
经过JS引擎解释后,上面的代码等同于下面的写法:
这个现象以前讨论过很多次,我就不完全复述作者原文了。
区块级别的声明
在函数里;
在一对{}里。
用let关键字有以下几个作用:
只存在于定义区块内;
不会再被提升,见代码:
不能重复使用同一个变量名;
但在不同区块里可以重复使用相同名声明变量,比如子区块,如:
常量的声明
它和let一样,也是区块作用域级别,并且不会被提升;
必须在声明时赋值;
不可以被修改,对象是特例,下面会说到;
也不能够重复使用相同的变量名,不论该名字是通过var还是let声明的。
常量对象
这个地方有点晦涩,作者解释的方法是,const关键字约束的是一个绑定本身,而不是被绑定的值,见代码:
这个现象很接近C++里的常量指针,指针指向的地址不能够改变,它指向一个对象后将永远指向它,但是这个对象本身的内容可以被修改。
临时无人区(TDZ)
TDZ这个概念只是在抽象层面存在的一个术语,用于方便解释一些JS引擎的行为,而且它不是ES规范里的内容,在JS引擎遇到var声明时,它的行为和ES3一样,提升变量,在遇到let或者const声明时,则是把变量先放到一个临时无人区,这样没有人能访问它,甚至是typeof,顺便提一下,在ES3里用typeof操作一个未定义变量是安全的,它会返回undefined。直到程序执行到该变量的声明,它的绑定才会被移出无人区,从此能够被访问到。
但TDZ有一个奇特的效果:
按说,在if区块之前不应该能够访问到value的。
循环里的区块绑定
ES3里,for循环有一个缺陷,如:
这也是个讲过很多次的经典问题了,就不多说了。ES6里,这个问题可以用let解决
循环里的函数
ES3里的问题如下示例代码:
这也是个经典的老问题了,我就不复述了。以前的解决办法是用闭包:
但是在ES6里,用let就行了。
这个行为也适用于for...in循环和for...of循环。
在普通的for循环里,你不能用const声明循环控制变量i,但是在for...in和for...of循环里,可以用const,效果和let一样:
全局作用域的绑定
不同于var,let和const在全局作用域里声明变量时,它们不再会在window对象上创建新属性,所以它们不会重写window上面的内建对象,比如Date和Math。
作者的建议
使用const,其次是let,抛弃var。
var声明与变量提升现象
这是前ES6时期var的问题,变量会被JS引擎处理成好像它们的声明被放在函数作用域(或者全局作用域)的顶端。function getValue(condition) { if (condition) { var value = "blue"; // other code return value; } else { // value exists here with a value of undefined return null; } // value exists here with a value of undefined }
经过JS引擎解释后,上面的代码等同于下面的写法:
function getValue(condition) { var value; if (condition) { value = "blue"; // other code return value; } else { return null; } }
这个现象以前讨论过很多次,我就不完全复述作者原文了。
区块级别的声明
let的声明
区块作用域的存在要符合两个要求:在函数里;
在一对{}里。
用let关键字有以下几个作用:
只存在于定义区块内;
不会再被提升,见代码:
function getValue(condition) { if (condition) { let value = "blue"; // other code return value; } else { // value doesn't exist here return null; } // value doesn't exist here }
不能重复使用同一个变量名;
但在不同区块里可以重复使用相同名声明变量,比如子区块,如:
var count = 30; // Does not throw an error if (condition) { let count = 40; // more code }
常量的声明
它和let一样,也是区块作用域级别,并且不会被提升;必须在声明时赋值;
不可以被修改,对象是特例,下面会说到;
也不能够重复使用相同的变量名,不论该名字是通过var还是let声明的。
常量对象
这个地方有点晦涩,作者解释的方法是,const关键字约束的是一个绑定本身,而不是被绑定的值,见代码:const person = { name: "Nicholas" }; // works person.name = "Greg"; // throws an error person = { name: "Greg" };
这个现象很接近C++里的常量指针,指针指向的地址不能够改变,它指向一个对象后将永远指向它,但是这个对象本身的内容可以被修改。
临时无人区(TDZ)
TDZ这个概念只是在抽象层面存在的一个术语,用于方便解释一些JS引擎的行为,而且它不是ES规范里的内容,在JS引擎遇到var声明时,它的行为和ES3一样,提升变量,在遇到let或者const声明时,则是把变量先放到一个临时无人区,这样没有人能访问它,甚至是typeof,顺便提一下,在ES3里用typeof操作一个未定义变量是安全的,它会返回undefined。直到程序执行到该变量的声明,它的绑定才会被移出无人区,从此能够被访问到。if (condition) { console.log(typeof value); // ReferenceError! let value = "blue"; }
但TDZ有一个奇特的效果:
console.log(typeof value); // "undefined" if (condition) { let value = "blue"; }
按说,在if区块之前不应该能够访问到value的。
循环里的区块绑定
ES3里,for循环有一个缺陷,如:for (var i = 0; i < 10; i++) { process(items[i]); } // i is still accessible here console.log(i); // 10
这也是个讲过很多次的经典问题了,就不多说了。ES6里,这个问题可以用let解决
for (let i = 0; i < 10; i++) { process(items[i]); } // i is not accessible here - throws an error console.log(i);
循环里的函数
ES3里的问题如下示例代码:var funcs = []; for (var i = 0; i < 10; i++) { funcs.push(function() { console.log(i); }); } funcs.forEach(function(func) { func(); // outputs the number "10" ten times });
这也是个经典的老问题了,我就不复述了。以前的解决办法是用闭包:
var funcs = []; for (var i = 0; i < 10; i++) { funcs.push((function(value) { return function() { console.log(value); } }(i))); } funcs.forEach(function(func) { func(); // outputs 0, then 1, then 2, up to 9 });
但是在ES6里,用let就行了。
var funcs = []; for (let i = 0; i < 10; i++) { funcs.push(function() { console.log(i); }); } funcs.forEach(function(func) { func(); // outputs 0, then 1, then 2, up to 9 })
这个行为也适用于for...in循环和for...of循环。
var funcs = [], object = { a: true, b: true, c: true }; for (let key in object) { funcs.push(function() { console.log(key); }); } funcs.forEach(function(func) { func(); // outputs "a", then "b", then "c" });
在普通的for循环里,你不能用const声明循环控制变量i,但是在for...in和for...of循环里,可以用const,效果和let一样:
var funcs = [], object = { a: true, b: true, c: true }; // doesn't cause an error for (const key in object) { funcs.push(function() { console.log(key); }); } funcs.forEach(function(func) { func(); // outputs "a", then "b", then "c" });
全局作用域的绑定
不同于var,let和const在全局作用域里声明变量时,它们不再会在window对象上创建新属性,所以它们不会重写window上面的内建对象,比如Date和Math。作者的建议
使用const,其次是let,抛弃var。
相关文章推荐
- 深入理解ES6--块级作用域(let const)
- 理解 ES6 语法中 yield* 关键字的作用
- 说说你对作用域链的理解
- 通俗理解神经网络中激活函数作用
- es6从入门到放弃之let,const,块级作用域
- GAC的理解及其作用
- 深入理解javascript中return的作用
- 对request,session,application作用域形象理解
- vue2.0使用ES6语法的箭头函数对this作用域的理解
- 深入理解Spring框架的作用
- 理解C++中的头文件和源文件的作用
- java 代理的作用 因素 以及对代理的理解
- Webpack 2 视频教程 012 - 理解Webpack 中的 CSS 作用域与 CSS Modules
- C#中理解接口以及接口的作用
- JavaScript ES6 Symbol.hasInstance的理解。
- 深入理解javascript原型和闭包(13)-【作用域】和【上下文环境】
- es6 代码片段理解
- 理解 JavaScript 作用域和作用域链
- 深入理解ES6 Promise 扩展always方法