附錄

原型繼承

除了 nullundefined 以外,每種原始資料類型都有 prototype 是對應的物件包裝函式,提供可運作的方法 。透過原始方法叫用方法或屬性查詢時 JavaScript 會包住在背景中的基本功能,並呼叫 方法或 而是對包裝函式物件進行屬性查詢。

例如,字串常值沒有自己的方法,但您可以呼叫 上的 .toUpperCase() 方法 (因為對應的 String 物件) 包裝函式:

"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL

這就是所謂的「原型繼承」,也就是繼承屬性和方法 來自特定值的對應建構函式。

Number.prototype
> Number { 0 }
>  constructor: function Number()
>  toExponential: function toExponential()
>  toFixed: function toFixed()
>  toLocaleString: function toLocaleString()
>  toPrecision: function toPrecision()
>  toString: function toString()
>  valueOf: function valueOf()
>  <prototype>: Object { … }

您可以使用這些建構函式建立基元,而不只是定義 讓他們按照價值觀舉例來說,使用 String 建構函式會建立 字串物件,而不是字串常值:這個物件不只包含我們的字串 值,但建構函式的所有繼承屬性和方法。

const myString = new String( "I'm a string." );

myString;
> String { "I'm a string." }

typeof myString;
> "object"

myString.valueOf();
> "I'm a string."

大多數情況下,產生的物件會做為我們先前 定義方式舉例來說 new Number 建構函式會產生包含所有方法和 如果 Number 原型的屬性,您會將數學運算子用於 建立這些物件時就和使用數字常值時一樣:

const numberOne = new Number(1);
const numberTwo = new Number(2);

numberOne;
> Number { 1 }

typeof numberOne;
> "object"

numberTwo;
> Number { 2 }

typeof numberTwo;
> "object"

numberOne + numberTwo;
> 3

由於 JavaScript 內建 原型繼承的特性意味著它們沒有實質的益處。建立中 使用建構函式進行原始物件也可能產生非預期的結果,因為 結果是 物件,而不是簡單的常值:

let stringLiteral = "String literal."

typeof stringLiteral;
> "string"

let stringObject = new String( "String object." );

stringObject
> "object"

這可能會使嚴格比較運算子的使用更加複雜:

const myStringLiteral = "My string";
const myStringObject = new String( "My string" );

myStringLiteral === "My string";
> true

myStringObject === "My string";
> false

自動分號插入 (ASI)

剖析指令碼時,JavaScript 解譯器可以使用稱為 自動分號插入 (ASI) 來修正省略例項 分號。如果 JavaScript 剖析器遇到不允許使用的權杖,就會 嘗試在符記前加上分號修正潛在的語法錯誤, ,即可滿足下列條件:

  • 每個符記都以換行符號分隔。
  • 該符記為 }
  • 上一個符記是 ),插入的分號則是結尾的分號 do...while 陳述式的半形分號。

詳情請參閱 ASI 規則

舉例來說,如果在下列陳述式之後省略半形分號,就不會導致 語法錯誤的原因:

const myVariable = 2
myVariable + 3
> 5

但 ASI 無法代表同一行有多個陳述式,如果發生以下情況: 在同一行寫入多個陳述式,請務必以 分號:

const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier

const myVariable = 2; myVariable + 3;
> 5

ASI 是嘗試修正錯誤,而不是創造的語法彈性 轉換為 JavaScript請務必視情況使用分號,以免仰賴 產生正確的程式碼

嚴格模式

規範 JavaScript 的編寫方式標準已大幅演進 任何在語言初期設計時所考量的重點每次變更 JavaScript 的預期行為必須避免在舊網站造成錯誤。

ES5 解決了一些長期沒有 JavaScript 語意的問題 導入「嚴格模式」來破壞現有實作項目,一種是 為整個指令碼或 個別函式。如要啟用嚴格模式,請使用字串常值 "use strict",在指令碼的第一行後面加上半形分號 函式:

"use strict";
function myFunction() {
  "use strict";
}

嚴格模式可防止特定「不安全」操作或已淘汰的功能、擲回 而不是常見的「靜音」錯誤禁止使用 可能與未來語言功能衝突的語法舉例來說 考量變數範圍的設計決策 開發人員可能因此誤以為以及全域範圍 宣告變數,無論其內容為何,方法是省略 var 關鍵字:

(function() {
  mySloppyGlobal = true;
}());

mySloppyGlobal;
> true

現代化的 JavaScript 執行階段無法修正這項行為,但風險可能會 將破壞性網站的連結錯誤或蓄意破壞。 反之,現代 JavaScript 會讓開發人員選擇嚴格限制,以防止這個行為 來建立新工作,並預設只在 新的語言功能不會中斷舊版的實作:

(function() {
    "use strict";
    mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal

您必須以下列形式寫入 "use strict" 「字串常值」。 範本常值 (use strict) 將無法運作。您也必須先包含 "use strict" 在預期環境中使用可執行的程式碼。否則,解譯器就會將其忽略。

(function() {
    "use strict";
    let myVariable = "String.";
    console.log( myVariable );
    sloppyGlobal = true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal

(function() {
    let myVariable = "String.";
    "use strict";
    console.log( myVariable );
    sloppyGlobal = true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope

依值列出

任何變數,包括物件的屬性 函式參數陣列 set,或 map,且可包含 值或參照值

將某個變數指派給另一個變數時,JavaScript 程式碼 引擎會建立該值的副本,並指派給變數。

將物件 (類別例項、陣列和函式) 指派給 變數,而非建立該物件的新副本,變數會包含 物件在記憶體中儲存位置的參照。因此,變更 變數參照的物件會改變參照的物件,而不只是 該變數包含的值例如,如果您初始化新的 變數,然後使用新的 變數,藉此將屬性新增至該物件,而會新增屬性及其值 原始物件:

const myObject = {};
const myObjectReference = myObject;

myObjectReference.myProperty = true;

myObject;
> Object { myProperty: true }

這不僅對於變更物件很重要 因為物件之間的一致性很嚴格,就需要同時將兩個變數 參照相同物件,評估為 true。無法參照 不同的物件,即使這些物件結構完全相同:

const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};

myObject === myNewObject;
> false

myObject === myReferencedObject;
> true

記憶體分配

JavaScript 使用自動記憶體管理功能,代表記憶體 明確分配或取消配置雖然 JavaScript 引擎的詳細資訊記憶體管理方法的 瞭解記憶體分配方式 處理參照值的背景資訊。

這裡有兩個「區域」「堆疊」和「堆積」堆疊儲存庫 靜態資料 (原始值和物件參照) 的靜態資料,因為 儲存這些資料所需的固定空間大小後, 指令碼執行時堆積會儲存物件,這些物件需要動態分配的空間 因為大小可能會在執行期間改變程序釋放記憶體 稱為「垃圾收集」這會移除沒有參照的物件 記憶體用量

主執行緒

JavaScript 基本上是單一執行緒語言,具備「同步」 執行模型,表示只能執行一項「工作」 逐步完成任務。這種依序執行的內容稱為「主要執行緒」

主執行緒是由其他瀏覽器工作共用,例如剖析 HTML、 轉譯及重新轉譯網頁的特定部分、執行 CSS 動畫 處理簡單 (如醒目顯示文字) 的使用者互動 複雜程度 (例如與表單元素互動的情況)瀏覽器供應商發現 最佳化主執行緒執行工作的方式,但較為複雜 指令碼仍會使用過多主執行緒的資源,影響整體 例如網頁效能

部分工作可以在以下位置執行: 名為 Web Workers 的背景執行緒。 ,但有一些限制:

  • 工作站執行緒只能對獨立的 JavaScript 檔案執行操作。
  • 這類功能大幅縮小或無法存取瀏覽器視窗和 UI。
  • 它們與主執行緒通訊的方式有所限制。

這些限制非常適合用於聚焦、資源密集型工作, 否則可能會佔用主執行緒

呼叫堆疊

用於管理「執行環境」的資料結構,也就是程式碼 是稱為呼叫堆疊的清單 (通常只有 「堆疊」)。首次執行指令碼時,JavaScript 解譯器 建立「全域執行環境」並推送至呼叫堆疊 語句中,從上到下會逐一執行 底部。當解譯器在執行 就會發出「函式執行環境」對該呼叫 在堆疊頂端,暫停全域執行環境,並執行函式 執行環境

每次呼叫函式時,該呼叫的函式執行內容為 並推送至堆疊頂端,正如目前的執行環境上方。 呼叫堆疊是在「先入先後順序」上運作也就是說 系統會執行近期堆疊中最高的函式呼叫,然後繼續執行 直到問題解決為止翻譯完成時,解譯器會移除該函式 以及包含該函式呼叫的執行環境 就會再次成為堆疊中的最高項目並繼續執行作業。

這些執行環境會擷取執行作業所需的任何值。他們 也會建立 Pod 要求範圍中可用的變數和函式 函式,並判斷及設定 this 關鍵字。

事件迴圈和回呼佇列

依序執行是指內含回呼的非同步工作。 例如從伺服器擷取資料、回應使用者互動 或是等候 setTimeoutsetInterval 設定的計時器,都會封鎖 主執行緒到工作完成為止,或意外中斷 回呼函式的執行環境當下的執行環境 就會將新加入堆疊。為解決這個問題,JavaScript 會管理非同步工作 使用以事件為準的「並行模式」由「事件迴圈」和 「回呼佇列」(有時也稱為「訊息佇列」)。

在主執行緒執行非同步工作時,回呼 函式的執行內容會放置在回呼佇列中,而不是位於 呼叫堆疊事件迴圈有時稱為 reactor 輪詢呼叫堆疊與回呼佇列的狀態。如果 Terraform 含有工作 回呼佇列和事件迴圈將判定呼叫堆疊為空白, 回呼佇列中的工作一次會推送至堆疊, 執行狀態