JavaScript >> Javascript 文檔 >  >> JavaScript

JavaScript 可變性能

關於 JavaScript 性能的最常見建議之一是盡可能偏愛局部變量而不是全局變量。至少只要我從事 Web 開發工作(九年),這個建議就已經存在並且從未受到質疑。該理論的基礎是 JavaScript 處理範圍和標識符解析的方式。

首先要記住的是,函數是 JavaScript 中的對象,因此創建函數實際上意味著創建一個對象來表示它。每個函數對像都有一個名為 [[Scope]] 的內部屬性 使用有關創建函數的範圍的信息進行初始化。 [[Scope]] property 實際上是一個可從函數的包含範圍訪問的變量對象列表。當你創建一個全局函數時,它是 [[Scope]] 屬性只有列表中的全局對象;在全局函數內部創建函數時,全局函數的激活對像在[[Scope]]的前面 其次是全局對象。

當一個函數被執行時,一個激活對像被創建,然後有一個與之關聯的作用域鏈。作用域鏈用於標識符解析,分兩步創建:

  1. 函數對象的[[Scope]]中的對象 屬性以相同的順序複製到作用域鏈中。
  2. 使用執行函數的變量創建一個新的激活對象。該對象包含 this 的定義 , arguments , 和局部變量(包括命名參數),並被推到作用域鏈的前面。

當遇到標識符時,將在執行上下文的作用域鏈中搜索具有匹配名稱的標識符。搜索從作用域鏈中的第一個對像開始,即函數的激活對象,然後繼續向全局對象前進,直到找到變量(如果從未找到變量,則以錯誤結束)。這是 ECMA-262 描述函數執行和標識符解析行為的方式,事實證明,這也是許多 JavaScript 引擎實現該語言的方式。請注意,ECMA-262 並不強制要求這種結構,它只是作為適當功能的描述提供。

鑑於對標識符解析的描述,局部變量應該比來自其他範圍的變量具有更快的標識符解析是有意義的,因為匹配名稱的搜索要短得多。但是快多少呢?為了回答這個問題,我設置了一系列使用不同範圍深度變量的測試。

我的第一個測試涉及向變量寫入一個簡單的值(字面意思是值 1)。結果很有趣。

從結果中可以清楚地看出,深度搜索標識符解析會帶來性能損失。標識符深度的每一次增加都顯示了執行的增加。毫無疑問,Internet Explorer 是同類產品中最差的(儘管 IE 8 確實顯示出一些改進)。在這種情況下,值得注意的例外是 Google Chrome 和最新的夜間 WebKit,因為即使標識符深度增加,它們的可變訪問時間也大致保持不變。這可以分別歸功於他們的下一代 JavaScript 引擎 V8 和 SquirrelFish。這些引擎執行優化以更快地運行代碼,並且顯然,這些優化使變量訪問比其他引擎快得多。 Opera 的表現令人欽佩,比 IE、Firefox 和當前的 Safari 版本快,但比 V8 和 Squirrelfish 驅動的瀏覽器慢。 Firefox 3.1 Beta 2 有點令人驚訝,因為局部變量寫入速度非常快,但一旦變量超出局部範圍,性能就會顯著下降。值得注意的是,我使用的是默認設置,因此沒有打開跟踪。

這些結果是針對變量寫入的,我想知道變量讀取的性能是否會有所不同。儘管出現了相同的趨勢,但變量讀取結果還是比寫入快了一些。

再一次,Internet Explorer 和 Firefox 是最慢的,Opera 表現出可觀的性能。再一次,Chrome 和最新的 WebKit 每晚顯示基於標識符深度的平坦性能。同樣值得注意的是,一旦您不再處理局部變量,Firefox 3.1 Beta 2 的變量訪問時間也會出現同樣奇怪的跳躍。

我在研究中發現的一件有趣的事情是 Chrome 在訪問全局變量時會降低性能。無論標識符深度如何,全局變量的訪問時間都保持不變,但該值比訪問具有相同標識符深度的局部變量所花費的時間長 50%。

這一切意味著什麼?這基本上意味著我的研究支持您應該盡可能使用局部變量的建議。在幾乎所有瀏覽器中,局部變量的讀寫速度都比包括全局變量在內的範圍外變量要快。您可以通過多種方式利用這些知識:

  • 觀察函數中使用的變量。如果您注意到一個函數多次使用超出範圍的變量,請將其存儲在局部變量中並改用它。您將將該變量的超出範圍標識符解析的數量減少到一個。這對於全局變量尤其重要,因為全局變量始終是作用域鏈中的最後一個對象。
  • 避免使用with 陳述。它通過在前面添加一個新的變量對象來臨時增加執行上下文的作用域鏈。這意味著在執行 with 期間,局部變量實際上移動到標識符深度為 2 ,施加性能損失。
  • 避免使用try-catch 如果你知道錯誤總會發生。 catch 子句以與 with 相同的方式擴充作用域鏈 陳述。在 try 中運行代碼不會受到懲罰 部分代碼,因此仍然建議使用 try-catch 用於處理意外錯誤。

如果您想圍繞這個主題進行更多討論,我在上個月的 Mountain View JavaScript Meetup 上做了一個簡短的演講。幻燈片可以在 SlideShare 上找到,整晚的視頻可以在 YouTube 上找到(我在 11 分鐘左右)。該視頻特別有趣,因為我的筆記本電腦一直表現不佳。

翻譯

  • 中文(簡體)

Tutorial JavaScript 教程
  1. 使用jQuery在數字前面添加額外的零?

  2. 前端開發者,你想過渡到全棧嗎?為什麼?

  3. 使用這個 Docker Compose 樣板設置 Express+Mongo API 服務器

  4. 如何使用 Gridsome 構建插件

  5. 為什麼我不再將 GraphQL 用於新項目

  6. 並行承諾執行:超越 Promise.all()

  7. 在其他學生回購中添加配置選項

  1. Show Dev:一個真正快速編寫 JS 箭頭函數的 VSCode 片段

  2. 如何在 Pug 和 Facebook JS SDK 中編寫多行腳本

  3. SitePoint Smackdown:PHP 與 Node.js

  4. 2022 年使用的 11 個頂級 React Native 組件庫

  5. 如何使用 API 在 Monaco Editor 中格式化 JSON 代碼?

  6. 使用 CSS3 和 MooTools 或 jQuery 創建爆炸徽標

  7. 無法獲取除“/”外的路由表達

  1. 我從 Twitch 上的實時編碼中學到的一些東西

  2. 10+ jQuery 預加載圖像插件

  3. 如何抓取靜態網站

  4. 使用語義發布自動化 npm 發布