不同上下文中JavaScript的this值
特别声明:此篇文章由Sunnylost根据Louis Lazaris的英文文章原名《JavaScript ‘this’ in Different Contexts》进行翻译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://www.impressivewebs.com/javascript-this-different-contexts以及作者相关信息
——作者:Louis Lazaris
——译者:Sunnylost
JavaScript拥有一个名为this 的关键字,在JavaScript程序中的不同位置,this所引用的值会有所不同。
在这篇文章中,我将通过实例来总结几种不同的上下文和this在这些上下文中究竟代表什么意思。注意,所有例子都会假定你在浏览器中开发客户端JavaScript。
在全局上下文中
当你在全局上下文中(也就是说不在任何函数中)引用this 会发生什么?
console.log(this === window); // true
你可以将上面那行代码输入到开发工具的控制台,这样便可以在任意页面进行测试。
就像上面那段代码演示的那样,全局上下文中的this 指向了window 对象。观察下面的代码以及随后的输出:
this.message = "Check this out."; window.message = "Check this out."; console.log(this.message); // "Check this out." console.log(window.message); // "Check this out." console.log(message); // "Check this out."
你可以 使用这个JS Bin来看到代码执行的效果。
那么这里又发生了什么?前两行代码其实都在做同样的事情——创建一个名为“message”的全局变量并将它的值设置为“Check this out.”接下来三条控制台的输出信息证明了这一事实,这些输出内容完全一样。
在函数上下文中
当你在函数或是闭包中引用了this 结果又会如何?
在这个没有任何嵌套的函数中,this值取决于你是否使用了严格模式:
function checkThisOut () { console.log(this === window); // true console.log(this); // object global } checkThisOut();
你可以看到,这与在全局上下文中没有任何变化(就是说this 依然引用了window对象)
function checkThisOut () { "use strict"; console.log(this === window); // false console.log(this); // undefined } checkThisOut();
如果我理解正确的话,正如Nicholas Zakas指出的,”use strict”例子展现的行为就是为了防止意外的声明全局变量。这里可能包含着更多的含义,我并没有完全理解,但看起来这就是两段代码之间最大的区别。
在模块模式中
最近我讨论了在写JavaScript时我目前正在使用的模块模式
让我们来看看在一个这种模式的上下文中 this 值引用了什么。
var s, PrimaryNameSpace = { settings : { first: true, second: false, foo: 'bar' }, init: function () { console.log(this); // references PrimaryNameSpace }, nextMethod: function () { }, anotherMethod: function () { } }; PrimaryNameSpace.init();
在模块模式上下文中,每一个单独方法(即函数)中,this 将引用整个模块。实际上,如果你在开发工具中查看在 init() 方法内生成的控制台信息,你将看到类似下面的内容:
控制台显示了一个对象(命名空间,或者模块),然后你深入挖掘这个对象就会看到不同的方法,以及所有定义在 settings 对象上的值。
因此,在 init() 方法中,你能够以下列方式使用 this 来引用模块及配置:
/* ... other code here ...*/ init: function () { s = this.settings; this.nextMethod(); this.anotherMethod(); console.log(s); }, /* ... other code here ...*/ PrimaryNameSpace.init();
使用 this 来引用其他方法和配置可以防止我们写出形式很长的引用。下面这段代码与上面写的内容是等价的:
/* ... other code here ...*/ init: function () { s = PrimaryNameSpace.settings; PrimaryNameSpace.nextMethod(); PrimaryNameSpace.anotherMethod(); console.log(s); }, /* ... other code here ...*/
在这两种情况下,控制台消息将显示为:
对于一个嵌套函数呢?
但是,如果你在众多方法之一里包含了一个匿名函数,那么 this就会发生变化,你将无法通过 this来访问模块对象。此时就需要书写长形式的语法,如下所示:
/* ... other code here ...*/ init: function () { s = this.settings; (function () { this.nextMethod(); // [object Window] has no method 'nextMethod' })(); }, /* ... other code here ...*/
我在上面代码中插入的注释是从控制台显示的信息,这表明了在匿名函数中, this 是全局“window”对象的引用,而不是 PrimaryNameSpace 对象的引用。
并且如先前所示,使用严格模式将使 this 值变成undefined而不是全局对象的引用。
总结
如果没有其他意外,这篇文章将证明使用 this 是一种非常有用的捷径。但你需要时刻谨记于心的是:上下文是如何改变 this 的值以及它在严格模式中的表现。
对于这个话题我仅仅讲了些皮毛,这里有一些文章进一步讨论了 this关键字:
如果你对我上面所说内容有更正的话,请将它们添加在留言中,让我能够对应更新。
译者手语:初次翻译前端技术博文,整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!
关于Sunnylost
08年开始自学java,09年加入北漂行列,从事Java Web工作,出于对JavaScript的喜爱,在2011年以外包身份进入新浪微博从事JS开发,目前就职于一淘。由于工作关系,逐步接触CSS,开始完善自身的前端技能。欢迎随时关注我:新浪微博
如需转载烦请注明出处:
英文原文:http://www.impressivewebs.com/javascript-this-different-contexts
中文译文:http://www.w3cplus.com/js/javascript-this-different-contexts.html