十分钟快速掌握版
在学习JS的过程中,我们经常会碰到一个概念就是JS的执行环境、作用域链。
今天我们用十分钟的时间,彻彻底底搞懂JS的执行环境、作用域链的概念。
JS执行环境
JS的执行环境分为两种:
1、全局环境
2、局部环境
全局环境
全局环境是最外围的执行环境,在Web浏览器中,全局环境被认为就是我们众所周知的window对象。因此所有的全局变量和函数都挂载在window对象下面。例如:
var name='DaMai';
window.name; //由于var name在全局环境中定义,所以是全局变量,输出:'DaMai'
function sayName(){
console.log(name); //由于name是全局变量,根据作用域链(这个稍后再讲)输出:'DaMai'
}
winsow.sayName //由于sayName在全局环境定义,所以sayName作为了全局方法挂载在window对象下,输出:function sayName()
window.sayName() //调用了sayName方法,输出:'DaMai'
复制代码
上面这个代码片段,其实很真切的让我们感受到了全局环境,在Web浏览器中,所有的变量声明和方法声明其实都被挂载在了window对象下。
局部环境
没有块级作用域
在讲局部环境之前,我相信很多同学像我一样学习过Java的同学,刚开始看到下面这个代码,会觉得不可思议。
for(var i=0;i<10;i++){
console.log(i); //依次输出:1,2,3,4,5,6,7,8,9
}
console.log(i); //输出:10
//不可思议的点,由于Js没有块级作用域的概念,所以其实var i还是在全局环境中定义的。
window.i; //输出:10。很好的说明了i是在全局环境中被定义,并被挂在了window对象下。
复制代码
这段代码片段,我其实想在讲局部环境之前,想给大家一个概念:
JS中没有块级作用域的概念,只有全局作用域和函数作用域,与之对应的就是全局环境和局部环境
由于没有块级作用域,所以for循环中的 var i=0
其实是在全局环境定义的。因此被挂在在window对象之下,所以我们通过 window.i;
能输出10。
回到正题,
局部环境(函数环境,更直观),每个函数都有自己的执行环境,当执行流进入该函数时,函数的执行环境就被推入到环境栈中,当函数代码执行完毕之后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁,环境栈就将该执行环境弹出,将控制权返回给之前的执行环境。
注释:全局环境直到应用退出,——例如关闭网页或者浏览器,才会被销毁。
这句话可能比较晦涩难懂,我用下面这个例子,直白的说明这句话:
var name='DaMai';
function getName(){
return name;
}
function sayName(){
var firstName='Da';
console.log(firstName);
console.log(getName()); //输出'DaMai',局部环境能访问全局环境中的变量和函数
console.log(name); //输出:'DaMai',局部环境能访问全局环境中的变量和函数
function sayFirstName(){
console.log(firstName);
}
sayFirstName();
}
console.log(name);//由于是全局环境声明的name变量,所以输出:'DaMai'
sayName(); //由于是在全局环境声明的方法,所以全局环境能调用,
//调用后执行函数代码,sayName局部环境进入环境栈,输出:'Da'、'DaMai'、'DaMai'和'Da'。
//在执行流执行完所有sayName的代码后,销毁sayName的局部环境,又回到了全局环境
console.log(firstName); //由于firstName是在局部环境中声明,所以当函数执行完毕后,
//sayName中定义的局部变量声明firstName和局部环境中的方法sayFirstName已经被销毁,
//在全局环境中已经无法访问
复制代码
上面这段代码很好说明了前面那段晦涩难懂的话,由于在局部变量中被定义的变量和函数,会在代码被执行完成后销毁;局部执行环境被弹出环境栈。
作用域链
作用域链其实可以理解成由于全局作用域和局部作用域、局部作用域和局部作用域相互嵌套,形成作用域链。各个作用域像链条一般串在了一起。当前执行的环境能够通过作用域链去访问链条上的变量和函数。
var red='red';
function sayColor(){
var blue='blue';
console.log(red); //当执行流在sayColor局部环境时,能够访问上一个环境,即全局环境window的参数和函数
console.log(blue); //同上
// console.log(green);//报错,因为当sayColor被调用,执行流在sayColor局部环境中时,
//不能访问下一个环境sayOtherColor,因为sayOtherColor还未进入环境栈
function sayOtherColor(){
var green='green';
//当sayOtherColor被调用时,执行流进入sayOtherColor环境,
//sayOtherColor环境被推入环境栈,
//所以此时的环境栈从顶部到底部依此是:sayOtherColor->sayColor->全局环境。
//根据作用域链的规则:内部环境能够通过作用域链访问所有外部环境的变量和函数。
//所以:sayOtherColor能够访问sayColor环境和全局环境中的变量和方法。
console.log(red);
console.log(blue);
console.log(green);
}
sayOtherColor(); //调用sayOtherColor
}
sayColor();
console.log(blue); //输出:ReferenceError: blue is not defined,因为外部环境不能访问内部环境的任何变量和函数
复制代码
将代码中的环境串成作用域链,如下图:
内部环境能够通过作用域链访问所有外部环境变量和函数,外部环境不能访问内部环境的任何变量和函数
内部环境能够通过作用域链访问所有外部环境变量和函数
所以我们可以根据上面那图来理解:
当sayColor函数被调用的时候,此时环境栈从顶部到底部:sayColor->全局环境, 所以sayColor能访问自身的变量 var blue='blue'
和自身函数 sayOtherColor()
以及外部环境的变量和函数,即全局环境的变量和函数。
当sayOtherColor函数被调用的时候,此时的环境栈从顶部到底部:sayOtherColor->sayColor->全局环境,所以sayOther能访问自身变量 var green='green'
以及通过作用域链,访问外部所有环境的变量和函数,即访问sayColor和全局环境的所有变量和函数。
外部环境不能访问内部环境的任何变量和函数
外部环境不能访问内部环境的任何变量和函数,所以在全局环境访问blue变量,会报 ReferenceError: blue is not
的错误。
同理,在sayColor环境访问green变量也会报这个错误。
标识符搜索沿着作用域链一级一级的向上搜索
var color="red";
function sayColor(){
var color="green";
console.log(color); //由于在局部环境搜索到color变量,则停止向上搜索,所以不会访问全局环境的color变量,
//输出:green
function sayHi(){
console.log('sayColor:sayHi');
}
sayHi(); //由于在局部环境搜索到sayHi()函数,则停止向上搜索,
//所以不会访问全局环境的color变量,输出:sayColor:sayHi
}
function sayHi(){
console.log('全局环境:sayHi');
}
sayColor();
console.log(color);
复制代码
由于在局部环境找到color标识符和sayHi标识符,所以局部作用域会停止向上搜索,因此输出: green
和 sayColor:sayHi
再看下一个例子:
var color="red";
function sayColor(){
console.log(color); //由于局部环境没有找到color标识符,向上查找,找到全局环境中的color变量,因此输出:'red'
sayHi(); //由于局部环境没有找到color标识符,向上查找,找到全局环境中的SayHi()函数,因此输出:'全局环境:sayHi'
}
function sayHi(){
console.log('全局环境:sayHi');
}
sayColor();
console.log(color); 输出:'red'
复制代码
由于在局部环境找到color标识符和sayHi标识符,所以局部作用域会向上搜索,在全局环境中找到了color变量和sayHi()函数,因此输出: red
和 全局环境:sayHi
作用域链总结:
1、作用域链其实就是全局作用域和局部作用域、局部作用域和局部作用域嵌套,形成一个作用域链条。环境通过作用域链可以访问链条上的变量和函数。
2、内部环境能够通过作用域链访问所有外部环境变量和函数,外部环境不能访问内部环境的任何变量和函数。
3、标识符搜索沿着作用域链一级一级的向上搜索
我是大麦,如果喜欢我的文章,请给我一个小心心。