理解 JS 回调函数中的 this

任何变量或对象都有其赖以生存的上下文。若是简单地将对象理解为一段代码,那么对象处在不一样的上下文,这段代码也会执行出不一样的结果。浏览器

例如,咱们定义一个函数 getUrl 和一个对象 pseudoWindowapp

function getUrl() {
    console.log(this.document.URL);
}

var pseudoWindow = {
    document: {
        URL: "I'm fake URL"
    },

    getUrl1: getUrl,

    getUrl2: function (callback) {
        callback();
        
        this.func = callback;
        this.func();
    }
}

执行 getUrl(),打印出当前页面的 URL。函数

执行 pseudoWindow.getUrl1(),打印出 I'm fake URLthis

执行 pseudoWindow.getUrl2(getUrl),先打印出当前页面 URL,后打印 I'm fake URLurl

下面让咱们用最简单粗暴的语言来解释以上代码。code

概念

什么是 this?

this 就是函数调用使用的上下文。对象

什么是上下文

上下文是在句号标记法中,句号前面的那个东西。ip

例如 pseudoWindow.getUrl1pseudoWindowpseudoWindow.getUrl1() 的上下文。作用域

什么是自由变量

当一个变量没有绑定到任何上下文时(或者说绑定到顶级做用域时,例如浏览器中的 window),它就是自由变量get

什么是变量对象

变量就是代码中你所用的标识符,一个标识符就是一个变量,多个变量可能指向同一个对象。例如:

pseudoWindow.getUrl1 === getUrl  // 获得 true

变量所处的上下文就是对象的做用域。

代码分解

调用 getUrl()

首先 getUrl 函数是定义在全局环境中,它是一个自由变量,在浏览器中(如下描述均为浏览器环境)它的上下文就是 window,因此 window.getUrl()getUrl() 是等价的。所以 this 指向 window 对象,打印出当前 URL。

调用 pseudoWindow.getUrl1()

首先 pseudoWindow 是一个对象,它能够充当上下文角色。咱们给它定义了一个属性 getUrl1,你能够将属性视为被绑定到某个上下文的变量,变量 getUrl1 自己又指向了变量 getUrl 所指向的对象,因此 pseudoWindow.getUrl1 === getUrl 才会为 true

当咱们调用 pseudoWindow.getUrl1() 时,它的意思是执行 getUrl() 这段代码,执行代码所需的参数为空,上下文为 pseudoWindow

因此函数中的 this 指向了 pseudoWindow,而 pseudoWindow 对象刚好又有 document 属性,该属性刚好又有 URL 属性,所以打印出 I'm fake URL

调用 pseudoWindow.getUrl2(getUrl)

同理咱们又定义了一个变量 getUrl2,并绑定到 pseudoWindow 对象身上,使之成为后者的一个属性。而这个属性自己又指向一个匿名函数,咱们姑且称之为 A,该函数对象接受另外一个函数对象做为回调函数。

所以执行 pseudoWindow.getUrl2(getUrl) 时,意思是执行代码 A,执行代码所需的参数为 getUrl 这段代码,上下文为 pseudoWindow

所以函数 A 中的 this 指向了 pseudoWindow

当程序执行到函数 A 内部的 callback() 时,由于变量 callback 没有绑定到任何上下文,所以它至关于一个自由变量,它的上下文就指向了 window 对象,所以首先打印出当前页面的 URL。

接下来 this.func = callback 意味着三件事:

  • 咱们新申明了一个变量 func
  • 经过 = 操做符,咱们将该变量指向了 callback 所指向的函数对象。
  • 经过 . 操做符,咱们将该变量绑定到了 this 对象上,使之成为后者的一个属性,而本例中 this 指向的就是 pseudoWindow 对象。

因而当程序执行到 this.func() 时,它的意思是执行 callback 这段代码,执行代码所需的参数为空,上下文为 pseudoWindow。因而打印出了 I'm fake URL

这段代码带来的一个反作用是咱们隐式地为 pseudoWindow 对象添加了一个新的属性 func,若是咱们想要经过回调的方式打印出 pseudoWindowdocument.URL 属性,又不想对 pseudoWindow 对象形成任何影响,那么咱们可使用函数的 apply 方法。全部函数都有 apply 方法,它会将它接收的第一个参数设置为函数的上下文。

例如本例中咱们能够改写代码成这样子:

var pseudoWindow = {
    document: {
        URL: "I'm fake URL"
    },

    getUrl1: getUrl,

    getUrl2: function (callback) {
        callback();        
        callback.apply(this);
    }
}

严格地说,你应该先检查 callback 参数类型是不是函数对象。

总结

Javascript 支持将函数做为参数传递,回调函数变量指向的函数对象都未与任何上下文绑定,全部未与明确上下文绑定的变量都是自由变量,浏览器器中全部自由变量的上下文都是 window 对象。

相关文章
相关标签/搜索