• JS的作用域和作用域链


    每个函数都有自己的作用域,当执行流进入一个函数时,函数就会被推入栈中,而在函数执行之后,栈将其执行环境弹出,把控制权放回给之前的作用域,全局作用域是最外围的一个作用域,因此,所有全局变量和函数都是作为window对象的属性和方法创建的。在某个方法函数的作用域中,所有代码执行完之后,该作用域被销毁,保存在其中的所有变量和函数定义也会随着被销毁,这就是局部作用域。

    (PS:全局作用域直到应用程序退出,例如关闭网页活浏览器,才会被销毁。)

    我个人理解的作用域链就是,当你声明一个函数时,局部作用域一级一级往上扣起来,就是作用域链。

    如图:

    作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。

    好了好了~  书面语言终于说完了。我们还是来看看代码吧~

    先来个基础点的:

    		var color = "blue";
    		function changeColor(){
    		  var anothercolor = "red";
    			if(color==="blue"){
    				color = anothercolor;
    			}
                           //这里可以访问anothercolor,color
                             
    		}
                    //这里只可以访问color
    		changeColor();
    		console.log(color);//red
    		console.log(anothercolor);// undefined
    

      解析:函数changeColor()的作用域链包含两个对象,它自己的变量对象和全局环境的变量对象。全局环境的变量对象(即window的对象)有color,changColor(),而changeColor()的变量对象是color,anothercolor。

    (用我自己的话来解释就是:父级不能访问子级的变量,但是子级可以父级的变量,祖父的变量,曾祖父的变量......哈哈~。并且我们执行流的访问顺序也是从子级开始,一级一级往上查找你的需要的变量,最后一级就是全局变量。)

    来看一下作用域链的典型栗子:

    var x = 10;
    
    	function foo(){
    
    		var y = 20;
    
    		function bar(){
    			var z = 30;
    
    			console.log(x+y+z);
    		};
    
    		bar();
    
    	};
    
    foo();//60
    

      解析:上面代码的输出结果是“60”,函数bar()可以直接访问"z",然后通过作用域链访问上层的“x”和“y”。

    大概基本概念弄清楚了吧?!~~

    那我要开始讲一些注意事情。。

    JS没有块级作用域。

     说明:在其他类C语言中,由{花括号}封闭的代码块都有自己的作用域,但是随和亲切的JS并非如此。

    举个栗子:

    if(true){
    	var color = "blue";
    }
    alert(color);//blue
    

      解析:如果在C,C++,或JAVA中,color会在if语句执行完毕被销毁。但在JS中,if语句中的变量声明会将变量添加到当前的作用域(这里的全局环境)。

    四不四觉得有点郁闷?怎么color这个变量还存在,不应该执行完之后,被销毁了吗?

    一开始小编也是很郁闷,再想想JS的执行流,小编目前的理解是:JS一切皆对象,只有函数方法可以封装,也只有函数的执行需要被调用,所以每个函数方法都有自己的作用域,而像if和for循环等等,这些直接执行的引用函数,所定义的变量都会被添加在与它同兄弟级的作用域中。

    现在再来看个栗子:

    for(var i = 0 ; i < 10;i++){
    	doSomething(i);
    }
    alert(i);//10
    

    解析:由for语句创建的变量i即使在for循环执行结束后,也依旧会存在于循环外部的执行环境中。

     结合作用域看闭包


    在JS中,闭包和作用域有紧密的关系。相信大家对下面的闭包栗子一定灰常熟悉,代码中通过闭包实现了一个简单的计算器。

    function counter(){
    	var x = 0;
    
    	return{
    		increase:function increase(){return ++x;},
    		decrease:function decrease(){return --x;}
    	};
    }
    
    var ctor = counter();
    
    console.log(ctor.increase());//1
    console.log(ctor.decrease());//0
    

      解析:四不四又纳闷了,怎么counter()函数退出了执行上下文栈,但是变量x还没有被销毁。闭包有三大特性:1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变量不会被垃圾回收机制回收。这里很明显,counter()函数利用闭包,把变量x引用到全局变量中。当var ctor = counter(),执行完毕后,ctor={increase:function(){...},decrease:function(){...}},这里需要注意的是,counter虽然退出了执行上下文栈,但是因为ctor中的成员仍然引用counter 的活动变量,所以counter的变量x依然在作用域中。

    (附上个人对闭包的理解:在函数嵌套函数中,子函数获取父函数的私有变量,通过return引用到外部的作用域中。)

    作用域链的主要作用就是用来变量查找,但是在JS中还有原型链的概念。

    于是对于二维作用域链查找可以总结为:当代码需要查找一个属性或者描述符的时候,首先会通过作用域链来查找相关的对象,一旦对象被找到,就会根据对象的原型链来查找属性。

    下面来举个栗子:

    var foo = {}
    function baz(){
    
    	Object.prototype.a = 'Set foo.a from prototype';
    
    	return function inner(){
    		console.log(foo.a);
    	}
    }
    baz()();//Set foo.a from prototype
    

      解析:对于这个栗子,流程是这样的,在inner()并没有发现foo,就通过作用域链去baz查找,也没有在baz里面找到作用域链,就去到全局环境,找到了foo,但是并没有找到属性a,于是就去到了foo._proto_的原型链中找到了属性a,便输出该值。

     文章说明:个人查看各种资料,原创所得,观点不一定准确,欢迎各路大牛勘误,小女子感激不尽。

  • 相关阅读:
    (转)Javascript的DOM操作
    (转)关于 Java 对象序列化您不知道的 5 件事
    java计算某个日期是什么节气(24节气)
    HTML5 客户端存储数据的两种方式
    laytpl : 一款非常轻量的JavaScript模板引擎
    解决阿里云服务器3306端口无法访问的问题(windows server 2008r2)
    阿里云服务器windows server2008r2+tomcat8.0+nginx1.12
    使用log4j2打印Log,log4j不能打印日志信息,log4j2不能打印日志信息,log4j和logj2,idea控制台信息乱码(文末)
    Android 实现http通信(servlet做服务器端) HttpClient、HttpURLConnection实现登录验证
    windows查看端口占用、结束进程
  • 原文地址:https://www.cnblogs.com/Yirannnnnn/p/4995549.html
Copyright © 2020-2023  润新知