提问者:小点点

“this”关键字在Nodejs和browser中的行为不同


我有这段代码:

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中,结果为:真假NodeJS结果

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

怎么会发生这种事?有人能解释一下引擎盖下面到底发生了什么吗?


共3个答案

匿名用户

在浏览器中,在全局范围内运行,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中的工作方式。在Node.js中,所有模块(脚本文件)都在它们自己的闭包中执行,而浏览器则直接在全局范围内执行所有脚本文件。

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

Globals文档中也提到了这一点:

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

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

因此,在函数外部,this是一个空对象,因为代码是按节点包装在函数中的,以便为每个模块(脚本文件)创建它自己的执行上下文;而在函数内部,因为它们是在没有指定执行上下文的情况下调用的,所以this是节点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确实是一个空对象,如上所述

为了完整起见,值得注意的是,在严格模式下,您将在浏览器中得到与在Node中相同的结果(true,false),但这是因为变量与它们在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总是一个对象,并且如果使用未定义nullthis-value(即没有特定的执行上下文)调用,它总是全局对象。

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

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

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

匿名用户

Node在此处将this显式设置为模块导出:

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

apply所做的是显式地固定this值(和参数)--在本例中,它将其设置为this.exports。例如,您可以执行:

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

节点重写是默认行为。但是,它必须使用global调用对象内部的函数--这是JS规范规定的。

匿名用户

在浏览器上下文中,最后一个指向Windowobject,它确实存在于节点上下文中。因此,最后一个是一个空对象。然而,在函数中出现的这种情况指向节点上下文中的某个全局对象。