第 23 章。標準全域變數
目錄
購買書籍
(廣告,請勿封鎖。)

第 23 章。標準全域變數

本章是 ECMAScript 規格標準化的全域變數的參考。Web 瀏覽器有更多全域變數,這些變數列於 MDN。所有全域變數都是全域物件的(自有或繼承的)屬性(瀏覽器中的window;請參閱全域物件)。

錯誤建構函式

有關這些建構函式的詳細資訊,請參閱錯誤建構函式

  • Error
  • EvalError
  • RangeError
  • ReferenceError
  • SyntaxError
  • TypeError
  • URIError

非建構函式函式

幾個全域函式不是建構函式。它們列於本節中。

編碼和解碼文字

下列函式處理 URI 的多種編碼和解碼方式:編碼和解碼:

encodeURI(uri)

百分比編碼uri中的特殊字元。特殊字元是所有 Unicode 字元,但下列字元除外

URI 字元

; , / ? : @ & = + $ #

也不編碼

a-z A-Z 0-9 - _ . ! ~ * ' ( )

例如

> encodeURI('http://example.com/Für Elise/')
'http://example.com/F%C3%BCr%20Elise/'
encodeURIComponent(uriComponent)

百分比編碼 uriComponent 中的所有字元,但下列字元除外

未編碼

a-z A-Z 0-9 - _ . ! ~ * ' ( )

encodeURI 相反,URL 和檔案名稱中重要的字元也會被編碼。因此,您可以使用此函式將任何文字轉換為合法的檔案名稱或 URL 路徑區段。例如

> encodeURIComponent('http://example.com/Für Elise/')
'http%3A%2F%2Fexample.com%2FF%C3%BCr%20Elise%2F'
decodeURI(encodedURI)

解碼由 encodeURI 產生的百分比編碼 URI

> decodeURI('http://example.com/F%C3%BCr%20Elise/')
'http://example.com/Für Elise/'

encodeURI 沒有編碼 URI 字元,而 decodeURI 沒有解碼 URI 字元,即使它們已正確編碼

> decodeURI('%2F')
'%2F'
> decodeURIComponent('%2F')
'/'
decodeURIComponent(encodedURIComponent)

解碼由 encodeURIComponent 產生的百分比編碼 URI 組件。與 decodeURI 相反,所有百分比編碼字元都會被解碼

> decodeURIComponent('http%3A%2F%2Fexample.com%2FF%C3%BCr%20Elise%2F')
'http://example.com/Für Elise/'

下列項目已不建議使用

  • escape(str) 百分比編碼 str。它已不建議使用,因為它無法正確處理非 ASCII 字元。請改用 encodeURIComponent()
  • unescape(str) 百分比解碼 str。它已不建議使用,因為它無法正確處理非 ASCII 字元。請改用 decodeURIComponent()

分類和剖析數字

下列方法有助於分類和剖析數字:

透過 eval() 和 new Function() 動態評估 JavaScript 程式碼

本節探討如何在 JavaScript 中動態評估程式碼。

使用 eval() 評估程式碼

函式呼叫:

eval(str)

評估 str 中的 JavaScript 程式碼。例如

> var a = 12;
> eval('a + 5')
17

請注意,eval() 會在陳述式內容中剖析 (請參閱 表達式與陳述式)

> eval('{ foo: 123 }')  // code block
123
> eval('({ foo: 123 })')  // object literal
{ foo: 123 }

在嚴格模式中使用 eval()

對於 eval(),您真的應該使用嚴格模式 (請參閱 嚴格模式)。在草率模式中,評估的程式碼可以在周圍的範圍中建立局部變數

function sloppyFunc() {
    eval('var foo = 123');  // added to the scope of sloppyFunc
    console.log(foo);  // 123
}

嚴格模式下無法發生

function strictFunc() {
    'use strict';
    eval('var foo = 123');
    console.log(foo);  // ReferenceError: foo is not defined
}

然而,即使在嚴格模式下,已評估的程式碼仍有讀取和寫入權限,可存取周圍範圍內的變數。若要防止此類存取,您需要間接呼叫 eval()

間接 eval() 會在全域範圍內評估

有兩種 呼叫 eval() 的方式:

  • 直接。透過直接呼叫名稱為「eval」的函式。
  • 間接。透過其他方式(透過 call(),作為 window 的方法,將其儲存在不同的名稱下並在那裡呼叫它,等等)。

正如我們已經看到的,直接 eval() 會在目前的範圍內執行程式碼

var x = 'global';

function directEval() {
    'use strict';
    var x = 'local';

    console.log(eval('x')); // local
}

相反地,間接 eval() 會在全域範圍內執行程式碼

var x = 'global';

function indirectEval() {
    'use strict';
    var x = 'local';

    // Don’t call eval directly
    console.log(eval.call(null, 'x')); // global
    console.log(window.eval('x')); // global
    console.log((1, eval)('x')); // global (1)

    // Change the name of eval
    var xeval = eval;
    console.log(xeval('x')); // global

    // Turn eval into a method
    var obj = { eval: eval };
    console.log(obj.eval('x')); // global
}

(1) 的說明:當您透過變數名稱參照變數時,初始結果是一個所謂的 參考,一個具有兩個主要欄位資料結構

  • base 指向 環境,儲存變數值的資料結構。
  • referencedName 是變數的名稱。

eval() 函式呼叫期間,函式呼叫運算子(括號)會遇到對 eval 的參考,並可以確定要呼叫的函式名稱。因此,此類函式呼叫會觸發直接 eval()。但是,您可以透過不給呼叫運算子參考來強制間接 eval()。這是在套用運算子之前擷取參考值的同時實現的。逗號運算子在第 (1) 行中為我們執行此操作。此運算子會評估第一個運算元,並傳回評估第二個運算元的結果。評估總是會產生值,這表示會解析參考,並遺失函式名稱。

間接評估的程式碼總是會很隨意。這是因為程式碼會獨立於其目前的環境進行評估

function strictFunc() {
    'use strict';

    var code = '(function () { return this }())';
    var result = eval.call(null, code);
    console.log(result !== undefined); // true, sloppy mode
}

使用 new Function() 評估程式碼

建構函式 Function()簽章:

new Function(param1, ..., paramN, funcBody)

它建立一個函式,其零個或多個參數具有名稱 param1parem2 等,且其主體為 funcBody;換句話說,建立的函式看起來像這樣

function («param1», ..., «paramN») {
    «funcBody»
}

讓我們使用 new Function() 建立一個函式 f,其傳回其參數的總和

> var f = new Function('x', 'y', 'return x+y');
> f(3, 4)
7

類似於間接 eval()new Function() 建立的函式其範圍是全域的:[18]

var x = 'global';

function strictFunc() {
    'use strict';
    var x = 'local';

    var f = new Function('return x');
    console.log(f()); // global
}

此類函式預設也是隨意的

function strictFunc() {
    'use strict';

    var sl = new Function('return this');
    console.log(sl() !== undefined); // true, sloppy mode

    var st = new Function('"use strict"; return this');
    console.log(st() === undefined); // true, strict mode
}

最佳實務

您應該避免 eval()new Function()。動態評估程式碼很慢,而且有潛在的安全風險。它也防止大多數使用靜態分析的工具(例如 IDE)考慮程式碼。

通常有更好的替代方案。例如,Brendan Eich 最近 推文一個反模式,由想要存取其名稱儲存在變數 propName 中的屬性的程式設計師使用

var value = eval('obj.'+propName);

這個想法有道理:點運算子僅支援固定的、靜態提供的屬性鍵。在這種情況下,屬性鍵僅在執行時才知道,這就是為什麼需要 eval() 來使用該運算子的原因。幸運的是,JavaScript 也有方括號運算子,它接受動態屬性鍵。因此,以下是前述程式碼的更好版本

var value = obj[propName];

您也不應該使用 eval()new Function() 來剖析 JSON 資料。那是危險的。請依賴 ECMAScript 5 內建的 JSON 支援(請參閱 第 22 章)或使用函式庫。

正當的使用案例

對於 eval()new Function() 有一些正當的、儘管進階的使用案例:具有函式的組態資料(JSON 不允許)、範本函式庫、直譯器、命令列和模組系統。

結論

這是動態評估 JavaScript 中程式碼的相對高階概觀。如果您想深入探討,您可以看看 kangax 的文章 “全域 eval。有哪些選項?”

主控台 API

在大部分 JavaScript 引擎中,都有一个全局对象 console,其中包含用于记录和调试的方法。该对象不是语言本身的一部分,但已成为事实上的标准。由于其主要目的是调试,因此 console 方法最常在开发期间使用,而在已部署代码中很少使用。

本部分概述了主控台 API。它记录了 Chrome 32、Firebug 1.12、Firefox 25、Internet Explorer 11、Node.js 0.10.22 和 Safari 7.0 中的现状。

主控台 API 在各个引擎中标准化程度如何?

主控台 API 的实现差异很大,并且不断变化。如果您需要权威的文档,则有两个选择。首先,您可以查看 API 的类标准概述:

其次,您可以查看各个引擎的文档

警告

Internet Explorer 9 中有一个错误。在该浏览器中,console 对象仅在开发人员工具至少打开过一次时才存在。这意味着如果您引用 console 而之前并未打开该工具,则会收到 ReferenceError。作为解决方法,您可以检查 console 是否存在,如果不存在,则创建一个虚拟实现。

简单记录

主控台 API 包含以下 记录方法:

console.clear()
清除主控台。
console.debug(object1, object2?, ...)
优先使用 console.log(),它与该方法执行相同操作。
console.error(object1, object2?, ...)
将参数记录到主控台。在浏览器中,记录的内容可能被标记为“错误”图标和/或包含堆栈跟踪或指向代码的链接。
console.exception(errorObject, object1?, ...]) [仅限 Firebug]
记录 object1 等,并显示交互式堆栈跟踪。
console.info(object1?, object2?, ...)
将参数记录到主控台。在浏览器中,记录的内容可能被标记为“信息”图标和/或包含堆栈跟踪或指向代码的链接。
console.log(object1?, object2?, ...)

將參數記錄到主控台。如果第一個參數是 printf 格式的字串,請使用它來列印剩餘的參數。例如 (Node.js REPL)

> console.log('%s', { foo: 'bar' })
[object Object]
> console.log('%j', { foo: 'bar' })
{"foo":"bar"}

唯一可靠的跨平台格式化指令是 %s。Node.js 支援 %j 將資料格式化為 JSON;瀏覽器傾向支援將某些互動內容記錄到主控台的指令。

console.trace()
記錄堆疊追蹤 (在許多瀏覽器中是互動式的)。
console.warn(object1?, object2?, ...)
將參數記錄到主控台。在瀏覽器中,記錄的內容可能會標記為「警告」圖示和/或包含堆疊追蹤或連結到程式碼。

下列表格指出在各種平台上的支援狀況

Chrome Firebug Firefox IE Node.js Safari

clear

debug

error

exception

info

log

trace

warn

exception 已以斜體排版,因為它僅在單一平台上受支援。

檢查和計數

主控台 API 包含下列檢查和計數方法:

console.assert(expr, obj?)
如果 exprfalse,請將 obj 記錄到主控台並擲回例外。如果它是 true,則不執行任何動作。
console.count(label?)
計數使用此標籤執行此陳述式的行數。

下列表格指出在各種平台上的支援狀況

Chrome Firebug Firefox IE Node.js Safari

assert

count

格式化記錄

主控台 API 包含下列用於格式化記錄的方法

console.dir(object)
將物件的表示形式列印到主控台。在瀏覽器中,可以互動式探索該表示形式。
console.dirxml(object)
列印 HTML 或 XML 元素的 XML 來源樹狀結構。
console.group(object1?, object2?, ...)
將物件記錄到主控台並開啟一個包含所有未來記錄內容的巢狀區塊。透過呼叫 console.groupEnd() 來關閉區塊。區塊最初會展開,但可以摺疊。
console.groupCollapsed(object1?, object2?, ...)
作用類似於 console.group(),但區塊最初會摺疊。
console.groupEnd()
關閉已由 console.group()console.group Collapsed() 開啟的群組。
console.table(data, columns?)

將陣列列印成表格,每一列一個元素。選用參數 columns 指定在欄中顯示哪些屬性/陣列索引。如果遺漏該參數,則所有屬性鍵都會用作表格欄。遺漏的屬性和陣列元素會在欄中顯示為 undefined

var persons = [
    { firstName: 'Jane', lastName: 'Bond' },
    { firstName: 'Lars', lastName: 'Croft', age: 72 }
];
// Equivalent:
console.table(persons);
console.table(persons, ['firstName', 'lastName', 'age']);

產生的表格如下

(索引) firstName lastName age

0

“Jane”

“Bond”

未定義

1

“Lars”

“Croft”

72

下列表格指出在各種平台上的支援狀況

Chrome Firebug Firefox IE Node.js Safari

dir

dirxml

group

groupCollapsed

groupEnd

table

剖析與計時

主控台 API 包含以下用於剖析與計時的函式:

console.markTimeline(label) [僅適用於 Safari]
console.timeStamp 相同。
console.profile(title?)
開啟剖析。選用的 title 用於剖析報告。
console.profileEnd()
停止剖析並列印剖析報告。
console.time(label)
開始計時器,其標籤為 label
console.timeEnd(label)
停止標籤為 label 的計時器,並列印從開始計時以來經過的時間。
console.timeStamp(label?)
記錄具有指定 label 的時間戳記。可能會記錄到主控台或時間軸。

下列表格指出在各種平台上的支援狀況

Chrome Firebug Firefox IE Node.js Safari

markTimeline

profile

(devtools)

profileEnd

(devtools)

time

timeEnd

timeStamp

markTimeline 以斜體排版,因為它僅在單一平台上受支援。(devtools) 標示表示必須開啟開發人員工具才能讓函式運作。[19]

命名空間與特殊值

以下全域變數用作函式的命名空間。有關詳細資訊,請參閱括號中指出的資料:

JSON
JSON API 功能 (第 22 章)
Math
Math API 功能 (第 21 章)
Object
元程式設計功能 (秘笈:使用物件)

以下全域變數包含特殊值。有關它們的詳細資訊,請檢閱 括號中指出的資料:

未定義

表示某個東西不存在的值 (undefined 和 null)

> ({}.foo) === undefined
true
NaN

表示某個東西「不是數字」(NaN)

> 1 / 'abc'
NaN
無限大

表示數值無限大 ∞ 的值 (Infinity)

> 1 / 0
Infinity



[18] Mariusz Nowak (@medikoo) 告訴我,預設情況下,由 Function 評估的程式碼到處都很馬虎。

[19] 感謝 Matthias Reuter (@gweax) 和 Philipp Kyeck (@pkyeck),他們為本節做出了貢獻。

下一章:24. Unicode 和 JavaScript