[英]Different behaviour of setTimeout in nodejs and Chrome
代碼示例是 -
global.a = 'aaa';
const obj = {
a: 'a',
desc() {
console.log(this);
console.log(this.a);
}
}
setTimeout(obj.desc, 2000)
當我在nodejs中運行此代碼時,我得到以下輸出:
Timeout {
_called: true,
_idleTimeout: 2000,
_idlePrev: null,
_idleNext: null,
_idleStart: 79,
_onTimeout: [Function: desc],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(asyncId)]: 6,
[Symbol(triggerAsyncId)]: 1 }
undefined
但相同的代碼,與global
改為window
在Chrome / Firefox的打印aaa
和window
對象,這是該MDN 文檔說,和這是我的期望。
我認為nodejs和Chrome都使用Google的v8 JS引擎來執行JavaScript。 那么為什么輸出會有所不同呢? 還有更多的東西嗎? 我試過搜索但找不到滿意的答案。
節點版本 - v9.10。
Chrome的版本 - 版本70.0.3538.110
node.js有自己的定時器實現,這與瀏覽器實現不同(盡管它們通常可以以相同的方式使用)。 這並沒有真正記錄,但是當你使用setTimeout
,它會創建一個Timer
類的實例,並將回調作為參數傳遞。 此回調被分配給Timer
實例的屬性,稍后將使用該屬性:
這意味着node.js中定時器的this
上下文順便設置為Timer實例本身。 瀏覽器顯然做了不同的事情。
這主要是原始代碼中的語義問題:當您從對象傳遞函數屬性時,它們會丟失其上下文。 您可以使用.bind
來保留上下文,或者使用另一個函數直接在obj
上調用desc
來保留上下文。
setTimeout(obj.desc.bind(obj), 2000);
setTimeout(() => obj.desc(), 2000);
這兩個將在node.js和瀏覽器環境中記錄obj
和obj.a
要回答你的問題,我們必須查閱定時器的源代碼,因為NodeJS的setTimeout
和vanilla setTimeout
不是一回事。
如果我們看這里 ,我們將找到setTimeout
的定義。 我們必須注意傳入的回調會發生什么:
它會傳遞到Timeout
構造函數中:
const timeout = new Timeout(callback, after, args, false);
好的,那么Timeout
課程是什么? 我們可以在這里找到。 注意這一行:
this._onTimeout = callback;
好吧,我們將回調粘貼在實例成員中,所以我懷疑它是如何被調用的。
如果我們回到另一個源文件並搜索_onTimeout
,我們會找到這一行:
timer._onTimeout();
所以在這種情況下,因為回調是如何被調用, timer
實際上是在什么this
指的是(因為你已經登錄注意到this
從回調),由於定時器(或Timeout
實例)沒有任何這樣的屬性( a
),它記錄undefined
。
在vanilla JS中,它的行為有所不同, this
指的是你的window
。
您可能知道,您可以通過簡單地將回調綁定到setTimeout
obj
來解決這種不一致問題,如下所示:
setTimeout(obj.desc.bind(obj), 2000);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.