作用域
在JavaScript中,我们可以将作用域定义为一套规则,这套规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称(变量名或者函数名)进行变量查找。
经过研究《高级程序设计》第3版的第四章,作用域相关的知识,自己总结并画了如下图,希望能让大家对作用域链有更好的理解,(可拖拽到新窗口查看大图)
总结: 作用域链:就是由多级作用域连续引用形成的链式结果;
它掌管着一切变量的使用顺序!(即:先在局部找,找不到,再沿着作用域链向父级找,作用域链的最末端永远是全局变量对象)
=========================================================================================================================
作用域小知识点:
es5里只有函数(局部)作用域和全局作用域,而没有块级作用域;和JAVA等语言不同
var arr = []; for (var i = 0; i < 5; i++) {
arr[i] = function () {
console.log(i);
};
} arr[0]();//
arr[1]();//
arr[2]();//
arr[3]();//
arr[4]();//
我们发现和我们预期的结果不一致,最终的结果都是5;
解决办法:
我们可以通过立即执行函数(闭包)的形式,即用函数作用域模拟出块级作用域的效果,我们把上面例子进行修改
var arr = []; for (var i = 0; i < 10; i++) {
(function(i) {
arr[i] = function () {
console.log(i);
};
})(i);
} arr[0]();//
arr[1]();//
arr[2]();//
arr[3]();//
arr[4]();//
es6里面新增了,const和let,这两个有块级作用域
像刚刚的for循环函数,我们就仅仅需要把 var i => 改为 let i 即可;代码如下:
var arr = []; for (let i = 0; i < 5; i++) {
arr[i] = function () {
console.log(i);
};
} arr[0]();//
arr[1]();//
arr[2]();//
arr[3]();//
arr[4]();//
const和let 与 var 有三大区别:
1、没有“变量提升hoisting”;
2、有“暂时性死区temporal dead zone”;
暂时性死区:
只要块级作用域内,存在let
命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
var tmp = 123; if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
上面代码中,存在全局变量tmp
,但是块级作用域内let
又声明了一个局部变量tmp
,导致后者绑定这个块级作用域,所以在let
声明变量前,对tmp
赋值会报错。
ES6明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
3、不允许在相同作用域内重复声明;
// 报错
function func() {
let a = 10;
var a = 1;
} // 报错
function func() {
let a = 10;
let a = 1;
}
建议:在使用ES6语法的项目中,不再使用var,使用let和const代替之。