簡體   English   中英

nodejs和Chrome中setTimeout的不同行為

[英]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的打印aaawindow對象,這是該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和瀏覽器環境中記錄objobj.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM