gpt4 book ai didi

javascript - 在node.js程序的全局范围内使用 'var'和 'this'

转载 作者:行者123 更新时间:2023-12-03 08:59:04 26 4
gpt4 key购买 nike

有人告诉我,全局作用域中的“this”表示全局对象,在全局作用域中使用“var”将定义全局对象中的属性。但是,我不知道为什么在 node.js 程序中使用“var”和“this”时会出现以下行为。我找到类似的stackoverflow question关于这一点,但我认为我的问题与提升无关。有人可以解释一下吗?谢谢。 (我使用的是node.js版本0.12.4)

yyy = 20;
global.zzz = 30;

var xxx = 10;
this.nnn = 40;

var v = function() {
//console.log(global); // <-- There is 'yyy' in the global object as expected
console.log(this.yyy); // <-- 20 as expected
console.log(yyy); // <-- 20 as expected

//console.log(global); // <-- There is 'zzz' in the global object as expected
console.log(this.zzz); // <-- 30 as expected
console.log(zzz); // <-- 30 as expected

//console.log(global); // <-- There is no 'xxx' in the global object. WHY??? where is 'xxx' located?
console.log(this.xxx); // <-- undefined as expected, because there is not 'xxx' in the global object.
console.log(xxx); // <-- 10. WHY??? where is 'xxx' located?

//console.log(global); // <-- There is no 'nnn' in the global object. WHY??? where is 'nnn' located?
console.log(this.nnn); // <-- undefined as expected, because there is not 'nnn' in the global object.
console.log(nnn); // <-- ReferenceError: nnn is not defined. WHY ReferenceError instead of 'undefined'???
}

v();

我将上面的代码包装到一个 HTML 文件中,如下所示,并在 Chrome(版本 44.0.2403.157 m)中进行测试。结果一切都如预期。 Node.js 中是否有我遗漏的重要概念?

<html>
<head></head>
<body>
<script>
yyy = 20;
window.zzz = 30; // change global.zzz to window.zzz

var xxx = 10;
this.nnn = 40;

var v = function() {
console.log(this.yyy); // <-- 20 as expected
console.log(yyy); // <-- 20 as expected

console.log(this.zzz); // <-- 30 as expected
console.log(zzz); // <-- 30 as expected

console.log(this.xxx); // <-- 10 as expected
console.log(xxx); // <-- 10 as expected

console.log(this.nnn); // <-- 40 as expected
console.log(nnn); // <-- 40 as expected
}

v();
</script>
</body>
</html>

==============更新=============

深入研究node.js源代码后,我想我已经理解了示例代码会产生这样的输出的原因。简单来说,当node.js通过“node.js xxx.js”启动xxx.js时,xxx.js会被node.js以“模块加载”的方式加载。

1)在src/node.js中的node.js源代码:

如果启动“node xxx.js”形式的javascript文件,node将使用Module.runMain()执行js文件;

} else if (process.argv[1]) {
...
} else {
// Main entry point into most programs:
Module.runMain();
}

2) 在 lib/module.js 中:

该文件中的调用流程为: "Module.runMain()"==> "Module._load()"==> "Module.load()"==> "Module._compile()"== >“compiledWrapper.apply(self.exports,...)”

// bootstrap main module.
Module.runMain = function() {
// Load the main module--the command line argument.
Module._load(process.argv[1], null, true);
...
};

Module._load = function(request, parent, isMain) {
...
try {
module.load(filename);
...
} finally {
...
}

Module.prototype.load = function(filename) {
...
Module._extensions[extension](this, filename);
...
}

Module._extensions['.js'] = function(module, filename) {
...
module._compile(stripBOM(content), filename);
};

NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];

Module.prototype._compile = function(content, filename) {
var self = this;
...
// create wrapper function
var wrapper = Module.wrap(content); // wrap in the above
// "NativeModule.wrapper"

var compiledWrapper = runInThisContext(wrapper, { filename: filename });
...
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);
}

由于“_compile()”函数位于Module对象的原型(prototype)结构中,因此“_compile()”中的“self”(或“this”)变量必须是Module本身。因此,“node xxx.js”的整个javascript文件加载过程就像执行一个函数,该函数的内容是xxx.js,而该函数中的“this”就是“Module.exports” (因为 Module.exports 是在 _compile() 末尾传递到“apply()”函数的第一个参数)

因此,整个加载过程相当于下面的javascript代码:

function (exports, require, module, __filename, __dirname) {
/* the content of xxx.js, and "this" would refer to "module.exports" */
}

了解了这一点后,就可以通过以下 2 个简单基本的 javascript 规则来理解原始示例代码:

  1. 如果函数在“xxx();”中调用,则函数中的“this”对象将是“全局对象”形式而不是“y.xxx();”形式。

  2. 为未声明的变量赋值会隐式将其创建为全局变量(它成为全局对象的属性)。

因此,示例代码逐行详细解释如下:

yyy = 20; // Assign a value to an undeclared variable implicitly creates
// it as a property of the global object, hence, "yyy" would be
// located in the global object.

global.zzz = 30; // explicitly add a property named "zzz" in the global
// object.

var xxx = 10; // "xxx" will be a local variable in the module, and is not
// assigned to the "module.exports". Hence, it actually acts as
// a local variable in the function implicitly created by
// node.js to load the file as a module. Due to lexical
// scoping rule, it can only be accessed from the function
// defined in this file (module).

this.nnn = 40; // This file is loaded as a module by "applying" a function
// and pass module.exports as the first argument of apply(),
// hence, the "this" here would refer to "module.exports".

console.log("this === module.exports? " + (this === module.exports)); // true

console.log(module); // you can see a "exports: { nnn: 40 }" lines in the
// module object

var v = function() {
// according to the call site syntax (v();), the "this" object here would
// be the global object.
console.log("this === global? " + (this === global)); // true

console.log(this.yyy); // <-- 20 as expected (global objects has "yyy",
// and "this" refers to the global object.
// Bingo~!)

console.log(yyy); // <-- 20 as expected (according to lexical
// scoping rule, it could find "yyy" in the
// global VariableObject (equals to global
// object). Bingo~!)

console.log(this.zzz); // <-- 30 as expected (global object has "zzz",
// and "this" refers to the global object.
// Bingo~!)

console.log(zzz); // <-- 30 as expected (according to lexical
// scoping rule, it could find "zzz" in the
// global VariableObject (equals to global
// object). Bingo~!)

console.log(this.xxx); // <-- undefined as expected ("xxx" is not
// defined in the global object, "xxx" is just a
// local variable in the function implicitly
// created by node.js to load this file, and
// "this" here refers to the module.exports.
// Hence, "undefined")

console.log(xxx); // <-- 10 as expected (according to lexical
// scoping rule, it could find "xxx" in the outer
// environment context of this function. Bingo~!)

console.log(this.nnn); // <-- undefined as expected ("nnn" is actually
// defined as a property of "module.exports", and
// "this" here refers to the global object,
// hence, "undefined")

console.log(nnn); // <-- ReferenceError: nnn is not defined.
// (according to the lexical scoping rule, it
// could not find any "variable name" equals to
// "nnn" in the scope chain. Because nnn is just
// the property of the module.exports, it could
// not be found in the lexical scoping searching.
// hence, "ReferenceError")
}

v();

最佳答案

这个问题有点误导,因为在全局上下文中,所有四对都返回相同的结果,无论是在 Node 中还是在浏览器中(将 window 替换为 global )。这是因为var在全局范围内没有意义,并在 this 上定义属性;自 this是全局对象,this.nnn , global.zzzyyy所有这些还定义了 global 上的属性。阅读时,nnn , zzzyyy不被识别为局部变量,因此它们在全局对象上查找(此处为 window 而不是 global,因此它将在 Stack Overflow 而不是 Node 中运行,但同样的原则也适用):

yyy = 20;
window.zzz = 30;

var xxx = 10;
this.nnn = 40;

var v = function() {
snippet.log(Object.keys(window));

snippet.log("this.yyy: " + this.yyy);
snippet.log("yyy: " + yyy);

snippet.log("this.zzz: " + this.zzz);
snippet.log("zzz: " + zzz);

snippet.log("this.xxx: " + this.xxx);
snippet.log("xxx: " + xxx);

snippet.log("this.nnn: " + this.nnn);
snippet.log("nnn: " + nnn);
};

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

但是,将其包含在某些非全局范围内,xxx 的情况就会发生变化。 - 注意它不再在全局对象上定义:

function vv() {

yyy = 20; // = this.yyy a.k.a. window.yyy
window.zzz = 30;

var xxx = 10; // local variable xxx
this.nnn = 40; // = window.nnn

var v = function() {
snippet.log(Object.keys(window));

snippet.log("this.yyy: " + this.yyy);
snippet.log("yyy: " + yyy);

snippet.log("this.zzz: " + this.zzz);
snippet.log("zzz: " + zzz);

snippet.log("this.xxx: " + this.xxx);
snippet.log("xxx: " + xxx);

snippet.log("this.nnn: " + this.nnn);
snippet.log("nnn: " + nnn);
};

v();

}

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

除了 this.xxx 之外,一切都保持不变/xxx部分。 this.xxx应该是undefined ,自 xxx是一个局部变量,不像其他三个变量那样在全局对象上定义。但是,作为局部变量,它会被该作用域中创建的任何函数捕获:我们说“v 超过 xxx ”,或“vxxx 上的闭包”,因此局部变量变量xxx可以从v看出.

事实上,更酷的是,闭包意味着在 v 范围内可见的所有局部变量。创建后可通过 v 看到同样,无论我们在哪里打电话 v来自 - 如以下代码片段所示:

var v;

function vv() {

yyy = 20; // = this.yyy a.k.a. window.yyy
window.zzz = 30;

var xxx = 10; // local variable xxx
this.nnn = 40; // = window.nnn

v = function() {
snippet.log(Object.keys(window));

snippet.log("this.yyy: " + this.yyy);
snippet.log("yyy: " + yyy);

snippet.log("this.zzz: " + this.zzz);
snippet.log("zzz: " + zzz);

snippet.log("this.xxx: " + this.xxx);
snippet.log("xxx: " + xxx);

snippet.log("this.nnn: " + this.nnn);
snippet.log("nnn: " + nnn);
};

}

vv();

// snippet.log("xxx before calling v: " + xxx); // ReferenceError
v();
// snippet.log("xxx after calling v: " + xxx); // ReferenceError
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

请注意,这里我们仍然可以读取 xxx10里面v ,即使我们调用函数v的地方不知道 xxx .

请注意,在 Node 中获取全局范围的唯一方法是交互执行程序 ( node < stuff.js );如果您将其作为文件执行( node stuff.js ),文件将在其自己的范围内执行,并且您将看不到此效果:

// a.js
var xxx = 10;
console.log(this.xxx);

$ node < a.js
10

$ node a.js
undefined

关于javascript - 在node.js程序的全局范围内使用 'var'和 'this',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32370806/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com