这个'这个'关键字在Nodejs和浏览器中的行为有所不同

时间:2016-01-17 13:08:22

标签: javascript node.js

我有这段代码:

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

这怎么可能发生?任何人都可以解释一下真正发生的事情吗?

6 个答案:

答案 0 :(得分:27)

在浏览器中,在全局范围内运行,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中,所有模块(脚本文件)都在自己的closure中执行,而浏览器直接在全局范围内执行所有脚本文件。

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

Globals documentation中也提到了这一点:

  

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

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

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

为了完整性,值得注意的是,在严格模式下,您在浏览器(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传递给严格模式的函数的值不会被强制成为对象(a.k.a。“boxed”)。
对于非严格模式下的正常函数,this始终是一个对象,如果调用并使用undefinednull,则它始终是全局对象 - 值,即没有特定的执行上下文。

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

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

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

答案 1 :(得分:4)

节点明确将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规范所强制的那样。

答案 2 :(得分:3)

在Browser-Context中,最后一个指向Windowobject,它存在于Node-Context中。因此最后这是一个空对象。然而,在函数中出现这种情况会指向Node-Context中的某个全局对象。

答案 3 :(得分:2)

差异非常简单

在节点环境中:

简称module.exportsexports。 但在函数引用整个 Node.js 包。

如果您登录控制台,可以看到以下内容:

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

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

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

在Node.js和浏览器环境中执行上一个示例,您将理解。

答案 4 :(得分:2)

我现在手上没有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]);
}

答案 5 :(得分:1)

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

enter image description here

注意:在 Node环境中,this的值 outside 表示该函数的this值为在外面 一个函数声明,但在由node.js创建的匿名函数包装器中,以实现 module 范围。