一点读书笔记

JavaScript中执行一段代码的时候,它会创建出对应的执行上下文。作为一个重要的概念,它定义了变量或者函数有权访问的其他数据,决定了他们各自的行为。对于每一个执行上下文,都有三个重要的属性:

变量对象

每个执行上下文都有一个与之对应的变量对象,它存储了在上下文中定义的变量和函数声明。当然在全局上下文中的变量对象就是全局对象了。

在处理一个执行上下文时有两个过程:

  1. 进入该执行上下文

这个时候是还没有执行代码,其中的变量对象中主要是函数的所有形参、函数声明和对象声明。

  1. 执行代码

这个时候代码会顺序执行,然后根据实际情况来修改变量对象。

举个栗子:

function demo(a) {
    var b = 2;
    function c() {};
    var d = function () {};
    b = 3;
}
demo(1);

在刚进入这个执行上下文的时候,变量对象是这样的:

AO = {
    arguments: {
        0: 3;
        length: 1;
    },
    a: 1,
    b: undefined,
    c: reference to function c() {},
    d: undefined
}

当开始执行代码的时候就是这样的:

AO = {
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,
    b: 3,
    c: reference to function c() {},
    d: reference to function d() {}
    
}

作用域链

计算机程序设计中,作用域(scope,或译作有效范围)是名字(name)与实体(entity)的绑定(binding)保持有效的那部分计算机程序。不同的编程语言可能有不同的作用域名字解析。而同一语言内也可能存在多种作用域,随实体类型变化而不同。作用域类别影响变量绑定方式,根据语言使用静态作用域还是动态作用域变量的取值可能会有不同的结果。

--维基百科

JavaScript在执行程序需要查找一个变量的时候,首先它会查看当前的执行上下文的变量对象,没有找到的话就去父级执行上下文的变量对象,要是还没有就去全局变量对象,直到找到。由多个执行上下文的变量对象构成的链表就是作用域链。

函数的作用域链在它创建的时候就决定了。在函数内部都有一个scope的属性,当我们创建的时候它就会把所有的父级的变量对象存储在里面。但是这并不是一条完整的作用域链,只有当程序进入该执行上下文,会把当前的变量对象添加到最前端,这是才是一条完整的作用域链。

this

简单看下this:

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

const obj = {
    a: 1,
    fun: demo
};
obj.demo();

const c = new demo();

对于直接调用函数demo(),里面的this则直接指向了window。而当对象obj来调用这个函数,那么this此时则就代表该对象。也就是谁调用就代表谁。而在最后使用new的话this就被永远绑在了c上面。而对于特殊的箭头函数,它本身没有this,它取决于包裹箭头函数的第一个普通函数。

使用bind()函数可以改变this的指向,它会创建一个新的函数并将this绑定传入的参数。

var obj = {
    a: 1,
    getA: function () {
        return this.a;
    }
};
var b = obj.getA.bind(obj);
console.log(b); // 1

当然这对箭头函数是无效的。