【问题标题】:The 'this' keyword behaves differently in Nodejs and browser'this' 关键字在 Nodejs 和浏览器中的行为不同
【发布时间】:2016-01-17 13:08:22
【问题描述】:

我有这段代码:

var obj1;
var obj2;

function x() {
    obj1 = this;
}

function y() {
    obj2 = this;
}

x();
y();

console.log(obj1 === obj2);
console.log(obj1 === this);

我使用命令行在 NodeJS 中运行此代码:node app.js 并在 Chrome 浏览器中作为脚本运行

结果:在 NodeJS 中,结果是:true false NodeJS result

在 Chrome 浏览器中,结果是:true true Browser result

这是怎么发生的?谁能解释一下幕后到底发生了什么?

【问题讨论】:

  • 您可能想打印出this 以查看它的实际情况。在浏览器中默认应该是window,在节点中我不确定它是什么。

标签: javascript node.js


【解决方案1】:

在浏览器中,在全局范围内运行,this 在您的示例中始终为 window

var obj1;
var obj2;

function x() {
    obj1 = this; // window
}

function y() {
    obj2 = this; // window
}

x();
y();

console.log(obj1 === obj2);  // window === window = true
console.log(obj1 === this);  // window === window = true

这不是它在 Node.js 中的工作方式。在 Node.js 中,所有模块(脚本文件)都在它们自己的 closure 中执行,而浏览器则直接在全局范围内执行所有脚本文件。

换句话说,在 Node 中运行的任何文件中,this 将只是一个空对象,因为 Node 将代码包装在一个立即调用的匿名函数中,并且您可以访问其中的全局范围改为GLOBAL 的上下文。

Globals documentation中也提到了这一点:

其中一些对象实际上不在全局范围内,而是在模块范围内 - 这将被注明。

但是,在 Node.js 中调用没有特定上下文的函数时,它通常会默认为 全局对象 - 与前面提到的 GLOBAL 相同,因为它是执行上下文。

所以在函数之外,this 是一个空对象,因为代码被 Node 包装在一个函数中,以便为每个模块(脚本文件)创建它自己的执行上下文,而在函数内部,因为它们是 在没有指定执行上下文的情况下调用,this 是 Node GLOBAL 对象

在 Node.js 中你会得到

var obj1;
var obj2;

function x() {
    obj1 = this; // GLOBAL
}

function y() {
    obj2 = this; // GLOBAL
}

x();
y();

console.log(obj1 === obj2);  // GLOBAL === GLOBAL = true
console.log(obj1 === this);  // GLOBAL === {} = false

最后一个this确实是一个空对象,如上所述


为了完整起见,值得注意的是,在严格模式下,您会在浏览器 (true, false) 中获得与在 Node 中相同的结果,但这是因为变量与它们在 Node 中的结果正好相反

"use strict"

var obj1;
var obj2;

function x() {
    obj1 = this; // undefined
}

function y() {
    obj2 = this; // undefined
}

x();
y();

console.log(obj1 === obj2);  // undefined === undefined = true
console.log(obj1 === this);  // undefined === window = false

这是因为作为 this 传递给严格模式下的函数的值不会被强制成为对象(也称为“装箱”)。
对于非严格模式下的普通函数,this 始终是一个对象,如果 调用 带有 undefinednull 这个值,即没有特定的值,它始终是全局对象执行上下文。

automatic boxing 不仅是性能成本,而且在浏览器中公开全局对象也是一种安全隐患,因为全局对象提供了对 JavaScript 环境必须限制的“安全”功能的访问。

因此对于严格模式函数,指定的this 不会被装箱到对象中,如果未指定,this 将是函数内部的undefined,如上所示,但this 仍将是窗口在全局范围内。

同样的事情发生在 Node.js 的严格模式下,其中函数内部的 this 不再是 GLOBAL 而是 undefined,而函数外部的 this 仍然是同一个空对象,并且最终结果仍然是true, false,但在 Node.js 的严格模式下this 的值也会有所不同。

【讨论】:

  • 有谁知道为什么 Node 在执行模块时使用空对象作为上下文? (我无法想象这是保护全局的尝试,因为调用函数时全局被用作上下文。)
  • @joeytwiddle - 我不知道,我猜运行的每个模块都只是放在(function() { code }()); 之类的东西中,有人决定将this 设为空对象,因为this 应该总是被定义,并且他们不希望它成为默认的全局对象。
  • 也许它试图保护全局,但是当调用函数时,他们仍然必须使用全局作为上下文对象,因为这是语言规范要求。
  • 您可能想提一下严格模式对此有何影响(双关语:)
【解决方案2】:

Node 将this 显式设置为模块导出here

const result = compiledWrapper.apply(this.exports, args);

apply 所做的是明确固定 this 值(和参数) - 在这种情况下 - 它将其设置为 this.exports。例如你可以这样做:

(function() { console.log(this.x); }).apply({x:3}); // alerts 3

节点覆盖是默认行为。但是,它必须使用 global 调用对象内部的函数——这是 JS 规范所要求的。

【讨论】:

  • 请注意,这最初是作为对 adeneo 答案的评论开始的 - 我决定,因为它代表了它本身,所以我将它作为单独的答案发布并支持他的答案。请参阅他的答案以了解更多推理,而不是“这是实现它的代码”。
【解决方案3】:

区别很简单

在节点环境中:

this指的是module.exports或简称exports。 但在函数 this 内部是指整个 Node.js 包。

如果你登录到控制台,你可以看到它:

function test(){
    console.log('this inside a function = ',this);
}

console.log('this outside a function = ',this);
test();

而在浏览器环境中,函数内部或函数外部的this指的是窗口对象,除非您使用new关键字这是另一个故事。

在 Node.js 和浏览器环境中执行前面的例子,你就会明白了。

【讨论】:

  • 在严格模式下 - 这将在函数内部未定义
【解决方案4】:

创建了一个表格来显示不同作用域模式下的this值:

注意:节点环境中,this的值在函数之外表示this的值在函数之外 一个函数声明,但 anonymous node.js 为实现 module 范围而创建的函数包装器。

【讨论】:

    【解决方案5】:

    在 Browser-Context 中,最后一个 this 指向 Window 对象,它确实存在于 Node-Context 中。因此最后一个 this 是一个空对象。然而,函数中 this 的出现指向 Node-Context 中的某个全局对象。

    【讨论】:

      【解决方案6】:

      我现在手头没有 Node.js 服务器,但我认为您可以自己研究这个问题,并为我们提供答案:D 请参阅下面的代码

      尝试运行:

      console.log(this.constructor.name+" "+obj1.constructor.name+" "+obj2.constructor.name);

      您还可以在函数中调试“父类”名称:

      function x() {
          console.log("x this: "+this.constructor.name);
          obj1 = this;
      }
      
      function y() {
          console.log("y this: "+this.constructor.name);
          obj2 = this;
      }
      

      对于视图对象方法/属性,您可以使用如下内容:

      for (a in obj2) {
          console.log("obj2." + a + " = " + obj2[a]);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多