[JS]大话执行环境和作用域链

十分钟快速掌握版

在学习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标识符,所以局部作用域会停止向上搜索,因此输出: greensayColor: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、标识符搜索沿着作用域链一级一级的向上搜索

我是大麦,如果喜欢我的文章,请给我一个小心心。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值