作弊小技巧:一招判断this所属上下文

Javascript的函数闭包,就是在调用函数时,通过返回一个携带该函数上下文的环境,获取本不可以被外部访问的数据。

下面是一个例子:


// Example 1

function a () {
    let x = 1
    a.__proto__.add = ()=> x++
    return function(){return x}
}

const x = a() // => ƒ (){return x}
x() // => 1
a.add() // or x.add()
x() // => 2

注意⚠️:正常的函数在执行完毕后,会通过GC回收函数里被创建的资源。但是因为通过返回函数闭包(包含函数上下文),导致在函数中创建的所有变量(即使它们原本的目的只是作为临时变量)都被保存了下来。

上下文

闭包由于有被执行函数的完整上下文,所以可以通过 this 关键字获取函数内对应的变量。如:


// Example 2

function b () {
    const x = 1
    this.value = function (){return x}
    return this
}

b().x // => undefined
b().value() // => 1

由于变量x并未挂载到上下文this上,所以在这里显示为未定义。

接下来,我们把问题复杂化一点,看下面这个例子,来感受一下上下文this作用域的变化。


// Example 3

function b () {
    b.x = 1
    b.__proto__.value = function (){return this.x}
    return function () {
        this.x = 2
        return this
    }
}

const a = b() // => ƒ () { this.x = 2; return this }

a.x = 123
a.value() // => 123

b.value() // => 1

执行a.value()时, this.x指向的是a.x;执行b.value()时, this.x指向的是b.x。如果在调用a.value()前不对a.x赋值,那么this.x指向undefined。

通过这个规律,我们其实可以发现,this指向的上下文,是由调用方提供的。

  • 而特别地:

a().x // => 2
new a() // => {x: 2}

1.当直接调用函数时(a()),此时函数a没有被任何对象调用,那么this指向一个全局对象Window(仅限在浏览器中)。所以在 Example 2 中的 this实际也是指向Window对象。

2.使用new关键字时,会生成一个空的上下文对象({}), 然后根据函数内部逻辑,将需要的数据挂载到其中,此时this指向这个新的对象。