• 深入javascript作用域链到闭包


    我之前用过闭包,用过this,虽然很多时候知道是这么一回事,但是确实理解上还不够深入。再一次看javascript高级程序设计这本书时,发现一起很多疑难问题竟然都懂了,所以总结一下一些理解,难免有错,所以希望有大神可以指出或者补充。

    一开始都是从作用域链开始的,要理解作用域链,首先要先说几个名词,一开始把我搞晕的几个名词。执行环境(execution context),变量对象(variable object),作用域链(scope chain)。

    执行环境,里面有是运行时的环境,包括了当前作用域有权访问的数据。在运行期间,环境的切换类似于堆栈,当从一个环境进入另一个作用域,那么就会把这个新的环境压入栈顶,当环境执行完毕,环境弹出,若有必要,改变量对象也会被摧毁。

    变量对象,顾名思义,这是一个对象,保存了当前执行环境中的所有变量以及函数。

    作用域链,上面的变量对象中,当一个环境被压入堆栈时,变量对象就会创建一个作用域链,这个作用域链的最前端,保存的是自己的变量对象,往后依次保存外部的每一个作用域的活动对象,而最后一个,当然就是全局对象,可以认为就是window。作用域链保证了在一个作用域变量的搜索的有序进行。

    当然,这样是很抽象的,这只是概念而已,没什么用,看下面这张图(感觉好low,不要建议)。

    如图所示,在js代码的执行过程中,我们知道,js是没有块级作用域的(可以通过模拟块级作用域),当从全局环境进入Fun1时,fun1环境压入栈中,它获得了当前的控制权,fun1的活动对象获得该作用域内的变量和属性,例如:this,arguments等等,Fun1环境的活动对象获得创建作用域链,这里只标明了作用域链,作用域链的最前端是自己的活动对象,也就是0,1是上级活动对象,在fun1中是window,也就是全局对象。同理,如果,fun1进入fun2,。。。到funN,funN的作用域链,而作用域链的收集是由上到下一级一级搜索。我们看看实际的例子:

    var name = 'window';
    console.log(name);
    function base()
    {
        var name = 'base';
        console.log(name);
        (function innerFun()
        {
            var name = 'in';
            console.log(name);
        })();  //自动运行方式,应该不难理解
    }
    base();
    

      我在不同的作用域内分别console各自的name,结果当然是不断获得当前的name:

    这段代码的运行过程可以抽象为这张图:

    即使没有这张图,我相信我们也能理解,但是它方便我们知道解释器做了什么,实际上这是在运行的时候发生的,一开始只有全局环境(也叫全局上下文),然后创建环境内的变量和函数,这里我们说创建Base的时候发生了什么,也就是执行到Base函数的时候,再看一张图:

    参考了书上的图,画的好丑哈哈。

    执行到创建Base函数的时候,穿件一个[[scope]]对象保存作用域链,这个与运行时不同,它没有包含自己的活动对象,当然,此时函数并未运行当然没有活动对象,但是它会搜索所有上级作用域,依次赋值到作用域链中。而到Base函数真正运行的时候,那就是上面那张图,解释器生成了一个运行时环境(context),压入栈中,用创建Base的作用域链初始化运行时作用域链,然后生成当前运行环境,压入作用域链前端,也就是当前作用域了,同理当在base中执行innerFun时,过程也是这样,只是这个时候,作用域链会更长了。

    实际上,了解这么细并不是很有必要,但是这有助于理解this和闭包,也有助于避免错误代码。

    关于this

    函数运行时,第一个初始化的变量时arguments,然后是this,我图中没画出来。this的值是函数运行时的对象。最简单的代码如下:

    function b()
    {
        this.name = "theSbWindow";
    }
    b();
    console.log(window.name);

    运行结果:

    用构造函数形式:

    有new运算符来new一个函数,结果当然不一样:

    function b()
    {
        this.names = "theSbWindow"; //我改成了names原因是这样更好理解,window有name的属性,但是没有names属性
    }
    new b();
    console.log(window.names);
    

     结果:

    很明显,此时this不是window,那是什么呢,new运算符发生了什么,其实是用已有的函数代码复制了一个对象,并且返回了这个对象,this指向了这个对象,我们只需要打印出来就知道了。

    function b()
    {
        this.names = "theSbWindow";
    }
    console.log(new b());  //改了这里
    console.log(window.names);

    结果:

    就是那个b了。

    当然,这有什么难,我们经常这有来创建对象,还用这种方式实现面向对象编程,额,没错。

    很好的说明了一切,this赋值给了window。如果是某个对象里面:

    name  = 'window';
    var me = {
        name:"wython",
        sayName: function ()
        {
            console.log(this.name);
    
        }
    };
    me.sayName();

    结果:wython

    当然,这有什么难。

    关于游离的函数中的This:


    什么叫游离的this,也就是没有明确是哪个对象的函数中的this,解释器会赋值给它window:

    name  = 'window';
    var me = {
        name:"wython",
        sayName: function ()
        {
            
            (function()
            {
                console.log(this.name);
            })(); //自动运行
        }
    };
    me.sayName();

    结果当然是window,为什么是这样,你可以想象在函数创建过程中,this赋值了window,活动对象中的this取得是window的值。

    name  = 'window';
    var me = {
        name:"wython",
        sayName: function ()
        {
            console.log(this.name);
            var b = {
                name : "b",
                sayName:function(){
                    console.log(this.name);
                }
            };
            b.sayName();
        }
    };
    me.sayName();

    如果是这样呢,当然,函数有明确的b对象,所以,运行结果是b。

    关于闭包:

    最简单的代码:

    function out()
    {
        var outName = "outter";
        return function(){
            return outName;
        }
    }
    var getOut = out();
    console.log(getOut());

    结果当然是:outter

    理解了作用域链在解释闭包就好解释了,我们只需要知道为什么out执行完毕以后竟然outName不被摧毁,根据上面的抽象图,当out执行完毕,out执行环境当然会被pop出栈

    out执行环境被摧毁,但是由于里面的函数引用了,作用域内的变量outName,所以暂时不被回收,也就是环境被摧毁,但是活动对象依然存活,仅仅是因为里面的函数的作用域链还保存着out的活动对象,使得他可以活到最后,当然,闭包也可能出现问题,但是只要注意下就行了。

  • 相关阅读:
    130被围绕的区域
    129求根到叶子节点数字之和
    单表查询
    数据的增删改
    多表结构的创建与分析
    修改表结构
    完整性约束
    基础操作和数据类型
    存储引擎
    Python3.8爬天气网站信息,并保存为CSV(11)
  • 原文地址:https://www.cnblogs.com/wuweixin/p/5289917.html
Copyright © 2020-2023  润新知