【问题标题】:Why does JavaScript "this" returns different values in Node and Browser environments?为什么 JavaScript “this” 在 Node 和 Browser 环境中返回不同的值?
【发布时间】:2015-09-24 11:56:44
【问题描述】:

在观看了 Kyle Simpson 在 Pluralsight 上的 Advanced JavaScript 课程后,我创建了一个简单的代码 sn-p 来尝试 this 绑定。我通常在 SublimeText 编辑器中工作,并在其中配置节点构建引擎,偶尔我也会在浏览器中运行相同的代码。在 Node 和浏览器 [Chrome] 中执行代码时,我注意到程序输出存在一个差异。

下面是代码 sn-p 来尝试this 绑定。

function foo() {
    console.log(this.bar);
}

var bar = "bar1";
var obj = {bar : "bar2"};

foo();
foo.call(obj);

当我使用 Sublime 执行它时,它返回 undefinedbar2。但是,当我在浏览器中执行相同的 sn-p 时,它会返回 bar1bar2,我认为这是正确的,因为变量 bar 是在全局范围内定义的。我无法理解为什么 Node 环境将其返回为 undefined。请帮忙。

【问题讨论】:

  • 这就是为什么所有的 Javascript 代码都应该在严格模式下运行。然后,错误的代码有时不会意外工作,但有时不会。 foo() 的调用以及随后在 foo() 内部对 this.bar 的引用只是糟糕的代码,有时会在浏览器中意外工作,因为您碰巧有一个与您尝试的属性同名的全局变量参考和this 在浏览器中设置为window,而不是在严格模式下运行,全局变量是window 对象的属性。在严格模式下运行,此错误将立即显示给您。
  • 基本上,在浏览器中 this === window,但在 Node 中 this !== global 和 Node 模块中的闭包之外的 var 无论如何都不会创建全局变量。
  • 非常感谢@jfriend00 的快速回复和澄清疑问。
  • 感谢@AlexanderO'Mara 的澄清。

标签: javascript node.js function binding this


【解决方案1】:

在 node.js 中,您的 bar 变量是该模块本地的模块变量。它不是全局变量。节点中的全局变量必须显式分配给global 对象。 node.js 模块中的顶级声明不会像浏览器 JS 文件中那样自动成为全局变量。从技术上讲,它们存在于由模块加载器创建的函数范围内,因此它们是模块函数范围的局部变量。

在 node.js 中,this 在像foo() 这样的普通函数调用中的值是global 对象。

但是,在 node.js 中,您的 bar 变量不是全局对象的属性。因此,当您尝试引用 this.bar 时,即为 global.bar,并且该名称的全局对象上没有任何属性。

因此,在 node.js 中,您实际上是在这样做:

// create a function which is like the module scope
function myModuleFunc() {
    function foo() {
        // the value of this in a plain function call in node.js is the
        // global object
        console.log(global.bar);
    }

    // this isn't a global
    var bar = "bar1";

    foo();
}

// execute that module scope function
myModuleFunction();

而且,希望你能明白为什么没有global.bar 属性,因此你得到undefined


这一切都意外地在浏览器中起作用,因为 var bar 是一个全局变量,而全局变量在 window 对象和 this === window 上,所以它意外地起作用。两个错误使一个正确有时有效。

正如我在评论中所说,强烈建议在严格模式下运行您的代码,有时事情不会意外工作,但其他情况不会 - 这个问题将是高度一致的,当您遇到时您会立即收到错误写错了代码。


虽然您说您已经了解浏览器环境中发生了什么,但由于您的问题是问为什么两者不同,所以我将描述浏览器情况以涵盖所有基础。

在浏览器中,this 在普通函数调用中的值(非严格模式时)是window 对象。

在浏览器中,您的bar 变量是一个全局变量,因此会自动成为window 对象的属性。

因此,在浏览器中,您实际上是在这样做:

function foo() {
    console.log(window.bar);
}

window.bar = "bar1";

foo();

因此,您可以看到为什么它会愉快地在 window 对象上创建一个属性,然后引用它。

【讨论】:

  • 这绝对是正确的答案,也是我的答案。 :) 另外,我想说的是,在其他情况下,Node 在this 行为上与浏览器不一致,例如setTimeout(..)。浏览器标准规定计时器应始终在全局(无论严格模式)下触发,但在 Node 中,它们违反了这一点,并且始终在专有(仅限节点)计时器对象的上下文中触发计时器。
  • @jfriend00 非常感谢您提供如此详细的解释。
【解决方案2】:

那是因为变量声明var bar = "bar1";,即声明在全局范围内。所有全局变量都可用作窗口对象的属性。

当您在没有任何上下文的情况下调用foo() 时,该方法的默认上下文是window 对象(在非严格模式下),因此您的代码执行变为console.log(window.bar);,正如我们在上面看到的bar 变量已将自身作为属性附加到窗口对象,因此它会打印值 bar1

function foo() {
  snippet.log('is window:' + (this instanceof Window))
  snippet.log('bar value: ' + this.bar);
}

var bar = "bar1";
snippet.log('window.bar: ' + window.bar)

var obj = {
  bar: "bar2"
};

foo();
foo.call(obj);
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

【讨论】:

  • 我对浏览器输出并不感到困惑,正如我所提到的,这似乎是正确的。 Node 环境将其返回为undefined,我不知道为什么,所以这个问题。
猜你喜欢
  • 2022-11-02
  • 2016-08-19
  • 2016-04-28
  • 2017-07-26
  • 1970-01-01
  • 2020-05-16
  • 2014-02-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多