我有这段代码:
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
这怎么可能发生?任何人都可以解释一下真正发生的事情吗?
答案 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
始终是一个对象,如果调用并使用undefined
或null
,则它始终是全局对象 - 值,即没有特定的执行上下文。
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.exports
或exports
。
但在函数中引用整个 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)