不同上下文中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();	

JS Bin 例子

你可以看到,这与在全局上下文中没有任何变化(就是说this 依然引用了window对象)

function checkThisOut () {
  "use strict";
  console.log(this === window); // false
  console.log(this); // undefined
}

checkThisOut();	

JS Bin demo

如果我理解正确的话,正如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();	

JS Bin demo

在模块模式上下文中,每一个单独方法(即函数)中,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();	

JS Bin demo

使用 this 来引用其他方法和配置可以防止我们写出形式很长的引用。下面这段代码与上面写的内容是等价的:

/* ... other code here ...*/

  init: function () {
    s = PrimaryNameSpace.settings;
    PrimaryNameSpace.nextMethod();
    PrimaryNameSpace.anotherMethod();
    console.log(s);
  },

/* ... other code here ...*/	

JS Bin demo

在这两种情况下,控制台消息将显示为:

在模块模式中

对于一个嵌套函数呢?

但是,如果你在众多方法之一里包含了一个匿名函数,那么 this就会发生变化,你将无法通过 this来访问模块对象。此时就需要书写长形式的语法,如下所示:

/* ... other code here ...*/

  init: function () {
    s = this.settings;
    (function () {
      this.nextMethod();
      // [object Window] has no method 'nextMethod'
    })();
  },

/* ... other code here ...*/	

JS Bin demo

我在上面代码中插入的注释是从控制台显示的信息,这表明了在匿名函数中, this 是全局“window”对象的引用,而不是 PrimaryNameSpace 对象的引用。

并且如先前所示,使用严格模式将使 this 值变成undefined而不是全局对象的引用。

总结

如果没有其他意外,这篇文章将证明使用 this 是一种非常有用的捷径。但你需要时刻谨记于心的是:上下文是如何改变 this 的值以及它在严格模式中的表现。

对于这个话题我仅仅讲了些皮毛,这里有一些文章进一步讨论了 this关键字:

  1. ‘this’ keyword on MDN
  2. Fully understanding the ‘this’ keyword
  3. JavaScript ‘this’ Gotchas

如果你对我上面所说内容有更正的话,请将它们添加在留言中,让我能够对应更新。

译者手语:初次翻译前端技术博文,整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!

关于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

返回顶部