十分钟快速掌握版
在学习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、标识符搜索沿着作用域链一级一级的向上搜索
我是大麦,如果喜欢我的文章,请给我一个小心心。