当前位置 博文首页 > 文章内容

    作用域与作用域链,原型及原型对象

    作者: 栏目:未分类 时间:2020-09-14 10:00:47

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    作用域

    作用域是在定义的时候就创建了, 而不是运行的时候。看看下面这个例子:

    let a=1
    function aa(){
        console.log(a)    //输出1
    }
    function bb(){
        let a=2
        aa()
    }
    
    

    是不是非常违背常理啊,你看嘛,aa在bb里面调用的,aa函数里面没有a变量,那么就应该去调用它的作 用域里找,刚好找到a等于2。
    思路是完美的,可是js的作者采用的静态作用域,不管你们怎么运行,你们 定义的时候作用域已经生成了。
    那么什么是作用域?
    变量和函数能被有效访问的区域或者集合。作用域决定了代码块之间的资源可访问性.
    作用域又分为全局作用域和函数作用域,块级作用域。 全局作用域任何地方都可以访问到,如window,Math等全局对象。
    函数作用域就是函数内部的变量和方法,函数外部是无法访问到的。 块级作用域指变量声明的代码段外是不可访问的,如let,const.

    作用域链

    作用域链表示一个作用域可以访问到变量的一个集合。函数作为一个对象有一个[[scope]]属性,就是表示这个集合的。再来理解几个概念词:
    AO:活动变量(Active object,AO)
    VO:变量对象(Variable object,VO)
    执行上下文:代码运行的环境,分为全局上下文和函数上下文。

    举例子来说明一下:(借用的例子)
    function a() {
            function b() {
                var b = 234;
            }
            var a = 123;
            b();
        }
        var gloab = 100;
        a();
    console.log(a)
    
    

    第一步: a 函数定义

    我们可以从上图中看到,a 函数在被定义时,a函数对象的属性[[scope]]作用域指向他的作用域链scope chain,
    此时它的作用域链的第一项指向了GO(Global Object)全局对象,我们看到全局对象上此时有5个属性,分别是this、window、document、a、glob。
    第二步: a 函数执行

    当a函数被执行时,此时a函数对象的作用域[[scope]]的作用域链scope chain的第一项指向了AO(Activation Object)活动对象,AO对象里有4个属性,
    分别是this、arguments、a、b。第二项指向了GO(Global Object),GO对象里依然有5个属性,分别是this、window、document、a、golb。
    第三步: b 函数定义

    当b函数被定义时,此时b函数对象的作用域[[scope]]的作用域链scope chain的第一项指向了AO(Activation Object)活动对象,AO对象里有4个属性,
    分别是this、arguments、a、b。第二项指向了GO(Global Object),GO对象里依然有5个属性,分别是this、window、document、a、golb。
    第四步: b 函数执行

    当b函数被执行时,此时b函数对象的作用域[[scope]]的作用域链scope chain的第一项指向了AO(Activation Object)活动对象,AO对象里有3个属性,
    分别是this、arguments、b。第一项指向了AO(Activation Object)活动对象,AO对象里有4个属性,分别是this、arguments、a、b。
    第二项指向了GO(Global Object),GO对象里依然有5个属性,分别是this、window、document、a、golb。
    ** 以上就是上面代码执行完之后的结果。**

    闭包

    闭包的官方定义:闭包是指那些能够访问自由变量的函数。
    我:一个作用域可以访问另一个作用域的变量,就产生闭包。
    闭包=函数+函数能够访问的自由变量。
    什么是自由变量?
    自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。

    var a = 1;
    
    function foo() {
        console.log(a);
    }
    
    foo()
    
    

    foo 函数可以访问变量 a,但是 a 既不是 foo 函数的局部变量,也不是 foo 函数的参数,所以 a 就是自由变量。

    var scope = "global scope";
    function checkscope(){
        var scope = "local scope";
        function f(){
            return scope;
        }
        return f;
    }
    var foo = checkscope();
    foo();
    输出 "local scope"
    
    

    因为变量查找的规则是通过作用域链的,作用域链是在函数定义的时候就已经确定了, 所以我们来看看定义f函数时候的[[scope]]属性:

    [
        AO:{
            scope:"local scope",
            f:function
        },
        global:{
         scope :"local scope",
         checkscope:function
        }
    ]
    
    

    f执行时候的[[scope]]属性:

    [
        AO:{
            arguments:[],
            this:window
        },
        AO:{
            scope:"local scope",
            f:function
        },
        global:{
         scope :"local scope",
         checkscope:function
        }
    ]
    根据先后顺序scope变量输出为"local scope"
    
    

    经典面试题:

    var data = [];
    
    for (var i = 0; i < 3; i++) {
      data[i] = function () {
        console.log(i);
      };
    }
    data[0]();
    data[1]();
    data[2]();
    //答案是都是 3,让我们分析一下原因:
    //当执行到 data[0] 函数之前,此时全局上下文的 VO 为:
    globalContext = {
        VO: {
            data: [...],
            i: 3
        }
    }
    //当执行 data[0] 函数的时候,data[0] 函数的作用域链为:
    data[0]Context = {
        Scope: [AO, globalContext.VO]
    }
    //data[0]Context 的 AO 并没有 i 值,所以会从 globalContext.VO 中查找,i 为 3,所以打印的结果就是 3
    

    原型及原型对象

    javascript万物皆对象,每个对象都有一个__proto__属性,指向了创造它的构造函数的原型对象。
    每个函数都有一个原型对象,prototype,当使用new 创造对象时继承这个对象。

    function A(){}
    var a=new A()
    a.__proto__===A.prototype
    
    

    下面就有问题了,谁创造了A这个构造函数呢,还有谁创造了A.prototype这个对象呢?
    这时候我们就要知道js两个顶级函数,Function,Object
    所有函数都是由Function创建的
    A.__proto__===Function.prototype
    刚说了所有函数都是由Function创建的,也包括自己。也就是说Function创造了自己:
    Function.__proto__===Function.prototype
    Object刚讲的是顶级函数,所以也是函数:
    Object.__proto__===Function.prototype
    所有的对象都是由Object构造函数创建的:
    A.prototype.__proto__===Object.prototype
    那么Object.prototype也是对象啊,是由谁创建的呢,记住万物皆空,何尝不是人生,到头来什么都会没有。
    Object.prototype.__proto__===null

    原型链

    1.在访问对象的某个成员的时候会先在对象中找是否存在

    2.如果当前对象中没有就在构造函数的原型对象中找

    3.如果原型对象中没有找到就到原型对象的原型上找

    4.直到Object的原型对象的原型是null为止