[英]Using 'var' and 'this' in the global scope in node.js program
有人告訴我,全局范圍中的“ this”表示全局對象,而在全局范圍中使用“ var”將定義全局對象中的屬性。 但是,我不知道為什么在node.js程序中使用“ var”和“ this”時會出現以下行為。 我發現與此類似的stackoverflow問題 ,但我認為我的問題與吊裝無關。 有人可以解釋嗎? 謝謝。 (我使用的是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時,node.js將以“模塊加載”方式加載xxx.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對象的原型結構中,因此“ _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規則來理解原始示例代碼:
如果在“ xxx();”中調用該函數,則該函數中的“ this”對象將是“ global object”。 形式而不是“ y.xxx();” 形成。
將值分配給未聲明的變量會隱式地將其創建為全局變量(它成為全局對象的屬性)。
因此,示例代碼的詳細解釋逐行如下:
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和(替換global
window
)中都相同。 這是因為var
在全局范圍內沒有意義,並在this
范圍內定義屬性; 由於this
是全局對象,因此this.nnn
, global.zzz
和yyy
都還定義了global
屬性。 讀取時, nnn
, zzz
和yyy
不被識別為局部變量,因此它們在全局對象上查找(此處是global
window
而不是global
window
,因此它將在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 aka 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
關閉”,或者“ v
是在xxx
上的閉合”,因此可以從v
看到局部變量xxx
。
實際上,即使更酷,閉包也意味着在創建v
的作用域內可見的所有局部變量也將被v
可見, 無論我們在何處調用v
from-如以下代碼段所示:
var v; function vv() { yyy = 20; // = this.yyy aka 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>
請注意,即使我們調用函數v的地方對xxx
並不了解,在這里我們仍然可以將xxx
讀為v
內的10
。
注意,在Node中獲取全局范圍的唯一方法是交互式執行程序( node < stuff.js
); 如果將其作為文件( node stuff.js
)執行,則文件將在其自己的范圍內執行,並且您將看不到這種效果:
// a.js
var xxx = 10;
console.log(this.xxx);
$ node < a.js
10
$ node a.js
undefined
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.