• 理解Javascript_15_作用域分配与变量访问规则,再送个闭包


    在阅读本博文之前,请先阅读《理解Javascript_13_执行模型详解》

    在'执行模型详解'中讲到了关于作用域分配的问题,这一篇博文将详细的说明函数对象、作用域链与执行上下文的关系。

    作用域分配与变量访问规则

      在 ECMAScript 中,函数也是对象。函数对象在变量实例化过程中会根据函数声明来创建,或者是在计算函数表达式或调用 Function 构造函数时创建。(关于'函数对象'请见《理解Javascript_08_函数对象》)。每个函数对象都有一个内部的 [[scope]] 属性,这个属性也由对象列表(链)组成。这个内部的[[scope]] 属性引用的就是创建它们的执行环境的作用域链,同时,当前执行环境的活动对象被添加到该对象列表的顶部。当我们在函数内部访问变量时,其实就是在作用域链上寻找变量的过程。

    理论性太强了(总结死我了!),还是让我们来看一段代码吧:

    <script type="text/javascript">
    function outer(){
    	var i = 10;
    	function inner(){
    		var j = 100;
    		alert(j);//100
    		alert(i);//10
    		alert(adf);
    	}
    	inner();
    }
    outer();
    </script>

    下图清晰的展现了上述代码的内存分配与作用域分配情况:

    下面来解释一下:

    1.载入代码,创建全局执行环境,此时会在可变对象(window)中添加outer变量,其指向于函数对象outer,此时作用域链中只有window对象.

    2.执行代码,当程序执行到outer()时,会在全局对象中寻找outer变量,成功调用。

    3.创建outer的执行环境,此时会新创建一个活动对象,添加变量i,设置值为10,添加变量inner,指向于函数对象inner.并将活动对象压入作用域链中.并将函数对象outer的[[scope]]属性指向活动对象outer。此时作用域链为outer的活动对象+window.

    4.执行代码,为 i 成功赋值。当程序执行到inner()时,会在函数对象outer的[[scope]]中寻找inner变量。找到后成功调用。

    5.创建inner的执行环境,新建一个活动对象,添加变量j,赋值为100,并将该活动对象压入作用域链中,并函数对象inner的[[scope]]属性指向活动对象inner.此时作用域链为:inner的活动对象+outer的活动对象+全局对象.

    6.执行代码为j赋值,当访问i、j时成功在作用域中找到对应的值并输出,而当访问变量adf时,没有在作用域中寻找到,访问出错。

    注:通过内存图,我们会发现作用域链与prototype链是如此的相象。这说明了很多问题...(仁者见仁智者见智,自己探寻答案吧!)

    闭包原理

    在我们了解了作用域的问题之后,对于闭包这个问题已经很简单了。什么是闭包?闭包就是封闭了外部函数作用域中变量的内部函数。

    我们来看一个典型的闭包运用:生成increment值

    <script type="text/javascript">
    var increment = (function(){
    	var id = 0;
    	return function(){
    		return ++id;
    	}
    })()
    
    alert(increment());//1
    alert(increment());//2
    </script>

    外层匿名函数返回的是一个内嵌函数,内嵌函数使用了外层匿名函数的局部变量id。照理外层匿名函数的局部变量在返回时就超出了作用域因此increment()调用无法使用才对。这就是闭包Closure,即函数调用返回了一个内嵌函数,而内嵌函数引用了外部函数的局部变量、参数等这些应当被关闭(Close)了的资源。这是怎么一回事呢?让我们来寻找答案:

    根据Scope Chain的理解可以解释,返回的内嵌函数已经持有了构造它时的Scope Chain,虽然outer返回导致这些对象超出了作用域、生存期范围,但JavaScript使用自动垃圾回收来释放对象内存: 按照规则定期检查,对象没有任何引用才被释放。因此上面的代码能够正确运行。

    参考:

  • 相关阅读:
    servlet的九大内置对象
    java中static、transient修饰的属性不能被序列化
    java 字节流与字符流的区别
    mac 下如何切换jdk的版本
    00 python基础--目录结构
    html 5 本地数据库-- Web Sql Database核心方法openDatabase、transaction、executeSql 详解
    shell脚本入门
    Canvas API详解
    精通CSS滤镜(filter)(实例解析)
    CSS中加号、星号及其他符号的作用
  • 原文地址:https://www.cnblogs.com/fool/p/1855265.html
Copyright © 2020-2023  润新知