任何变量或对象都有其赖以生存的上下文。若是简单地将对象理解为一段代码,那么对象处在不一样的上下文,这段代码也会执行出不一样的结果。浏览器
例如,咱们定义一个函数 getUrl
和一个对象 pseudoWindow
。app
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 URL
。this
执行 pseudoWindow.getUrl2(getUrl)
,先打印出当前页面 URL,后打印 I'm fake URL
。url
下面让咱们用最简单粗暴的语言来解释以上代码。code
this 就是函数调用使用的上下文。对象
上下文是在句号标记法中,句号前面的那个东西。ip
例如 pseudoWindow.getUrl1
,pseudoWindow
是 pseudoWindow.getUrl1()
的上下文。作用域
当一个变量没有绑定到任何上下文时(或者说绑定到顶级做用域时,例如浏览器中的 window),它就是自由变量。get
变量就是代码中你所用的标识符,一个标识符就是一个变量,多个变量可能指向同一个对象。例如:
pseudoWindow.getUrl1 === getUrl // 获得 true
变量所处的上下文就是对象的做用域。
首先 getUrl
函数是定义在全局环境中,它是一个自由变量,在浏览器中(如下描述均为浏览器环境)它的上下文就是 window
,因此 window.getUrl()
和 getUrl()
是等价的。所以 this
指向 window
对象,打印出当前 URL。
首先 pseudoWindow
是一个对象,它能够充当上下文角色。咱们给它定义了一个属性 getUrl1
,你能够将属性视为被绑定到某个上下文的变量,变量 getUrl1
自己又指向了变量 getUrl
所指向的对象,因此 pseudoWindow.getUrl1 === getUrl
才会为 true
。
当咱们调用 pseudoWindow.getUrl1()
时,它的意思是执行 getUrl()
这段代码,执行代码所需的参数为空,上下文为 pseudoWindow
。
因此函数中的 this
指向了 pseudoWindow
,而 pseudoWindow
对象刚好又有 document
属性,该属性刚好又有 URL
属性,所以打印出 I'm fake URL
。
同理咱们又定义了一个变量 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
,若是咱们想要经过回调的方式打印出 pseudoWindow
的 document.URL
属性,又不想对 pseudoWindow
对象形成任何影响,那么咱们可使用函数的 apply
方法。全部函数都有 apply
方法,它会将它接收的第一个参数设置为函数的上下文。
例如本例中咱们能够改写代码成这样子:
var pseudoWindow = { document: { URL: "I'm fake URL" }, getUrl1: getUrl, getUrl2: function (callback) { callback(); callback.apply(this); } }
严格地说,你应该先检查 callback 参数类型是不是函数对象。
Javascript 支持将函数做为参数传递,回调函数变量指向的函数对象都未与任何上下文绑定,全部未与明确上下文绑定的变量都是自由变量,浏览器器中全部自由变量的上下文都是 window 对象。