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

作用域

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

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为止

posted @ 2020-09-14 09:55  爱喝可乐的靓仔  阅读(236)  评论(0编辑  收藏  举报