阜陽網站開發(fā)招聘seo營銷服務
第二十一章:數學
原文:21. Math
譯者:飛龍
協議:CC BY-NC-SA 4.0
Math
對象用作多個數學函數的命名空間。本章提供了一個概述。
數學屬性
Math
的屬性如下:
Math.E
歐拉常數(e)
Math.LN2
2 的自然對數
Math.LN10
10 的自然對數
Math.LOG2E
e 的底數 2 對數
Math.LOG10E
e 的十進制對數
Math.PI
圓的周長與直徑的比值(3.14159 …),π
Math.SQRT1_2
一半的平方根,
Math.SQRT2
二的平方根,
數值函數
Math
的數值函數包括以下內容:
Math.abs(x)
返回x
的絕對值。
Math.ceil(x)
返回大于等于x
的最小整數:
> Math.ceil(3.999)
4
> Math.ceil(3.001)
4
> Math.ceil(-3.001)
-3
> Math.ceil(3.000)
3
有關將浮點數轉換為整數的更多信息,請參閱轉換為整數。
Math.exp(x)
返回 e^x,其中 e 是歐拉常數(Math.E
)。這是Math.log()
的反函數。
Math.floor(x)
返回小于等于x
的最大整數:
> Math.floor(3.999)
3
> Math.floor(3.001)
3
> Math.floor(-3.001)
-4
> Math.floor(3.000)
3
有關將浮點數轉換為整數的更多信息,請參閱轉換為整數。
Math.log(x)
返回x
的自然(以 e 為底)對數 ln(x
)。這是Math.exp()
的反函數。
Math.pow(x, y)
返回 x^y,x
的y
次冪:
> Math.pow(9, 2)
81
> Math.pow(36, 0.5)
6
Math.round(x)
返回x
四舍五入到最接近的整數(如果在兩個整數之間,則為較大的整數):
> Math.round(3.999)
4
> Math.round(3.001)
3
> Math.round(3.5)
4
> Math.round(-3.5)
-3
有關將浮點數轉換為整數的更多信息,請參閱轉換為整數。
Math.sqrt(x)
返回,
x
的平方根:
> Math.sqrt(256)
16
三角函數
三角函數方法接受弧度作為角度并返回。以下函數向您展示了如何實現轉換,如果需要的話:
-
從度到弧度:
function toRadians(degrees) {return degrees / 180 * Math.PI; }
以下是交互:
> toRadians(180)3.141592653589793> toRadians(90)1.5707963267948966```+ 從弧度到度:```jsfunction toDegrees(radians) {return radians / Math.PI * 180;}```以下是交互:```js> toDegrees(Math.PI * 2)360> toDegrees(Math.PI)180```三角函數方法如下:`Math.acos(x)`返回`x`的反余弦值。`Math.asin(x)`返回`x`的反正弦值。`Math.atan(x)`返回`x`的反正切值。`Math.atan2(y, x)`返回商的反正切值。`Math.cos(x)`返回`x`的余弦值。`Math.sin(x)`返回`x`的正弦值。`Math.tan(x)`返回`x`的正切值。## 其他函數以下是剩余的`Math`函數:`min(x1?, x2?, ...)`返回參數中的最小數:```js
> Math.min()
Infinity
> Math.min(27)
27
> Math.min(27, -38)
-38
> Math.min(27, -38, -43)
-43
通過apply()
在數組上使用它(參見func.apply(thisValue, argArray)):
> Math.min.apply(null, [27, -38, -43])
-43
max(x1?, x2?, ...)
返回參數中的最大數:
> Math.max()
-Infinity
> Math.max(7)
7
> Math.max(7, 10)
10
> Math.max(7, 10, -333)
10
通過apply()
在數組上使用它(參見func.apply(thisValue, argArray)):
> Math.max.apply(null, [7, 10, -333])
10
Math.random()
/*** Compute a random integer within the given range.** @param [lower] Optional lower bound. Default: zero.* @returns A random integer i, lower ≤ i < upper*/
function getRandomInteger(lower, upper) {if (arguments.length === 1) {upper = lower;lower = 0;}return Math.floor(Math.random() * (upper - lower)) + lower;
}
第二十二章:JSON
原文:22. JSON
譯者:飛龍
協議:CC BY-NC-SA 4.0
JSON(JavaScript 對象表示)是一種用于數據存儲的純文本格式。它已經成為 Web 服務、配置文件等數據交換格式的一種流行選擇。ECMAScript 5 有一個 API,用于將 JSON 格式的字符串轉換為 JavaScript 值(解析)以及反之(字符串化)。
背景
本節(jié)解釋了 JSON 是什么以及它是如何創(chuàng)建的。
數據格式
JSON 將數據存儲為純文本。它的語法是 JavaScript 表達式語法的子集。例如:
{"first": "Jane","last": "Porter","married": true,"born": 1890,"friends": [ "Tarzan", "Cheeta" ]
}
JSON 使用 JavaScript 表達式中的以下結構:
復合
JSON 數據的對象和 JSON 數據的數組
原子
字符串、數字、布爾值和空值
它遵循以下規(guī)則:
-
字符串必須始終用雙引號括起來;例如,像
'mystr'
這樣的字符串字面量是非法的。 -
屬性鍵必須用雙引號括起來
歷史
Douglas Crockford 于 2001 年發(fā)現了 JSON。他給它起了個名字,并在json.org
上發(fā)布了一個規(guī)范:
我發(fā)現了 JSON。我不主張發(fā)明 JSON,因為它已經存在于自然界中。我所做的是發(fā)現它,我給它起了名字,我描述了它的有用之處。我不主張自己是第一個發(fā)現它的人;我知道至少有其他人在我之前至少一年發(fā)現了它。我發(fā)現的最早的情況是,Netscape 有人在至少 1996 年就開始使用 JavaScript 數組文字進行數據通信,而這至少比我想到這個想法早了五年。
最初,Crockford 希望 JSON 有一個名字叫JavaScript 標記語言,但是 JSML 的首字母縮寫已經被JSpeech 標記語言使用了。
JSON 規(guī)范已經被翻譯成許多人類語言,現在有許多編程語言的庫支持解析和生成 JSON。
語法
Douglas Crockford 創(chuàng)建了一張 JSON 名片,正面有一個徽標(參見圖 22-1),背面有完整的語法(參見圖 22-2)。這使得 JSON 的簡單性在視覺上顯而易見。
圖 22-1:JSON 名片的正面顯示了一個徽標(來源:Eric Miraglia)。
圖 22-2:JSON 名片的背面包含完整的語法(來源:Eric Miraglia)。
語法可以轉錄如下:
object{ }{ members }memberspairpair , memberspairstring : valuearray[ ][ elements ]elementsvaluevalue , elementsvaluestringnumberobjectarraytruefalsenullstring""" chars "charscharchar charscharany-Unicode-character-except-"-or-\-or-control-character\" \\ \/ \b \f \n \r \t\u four-hex-digitsnumberintint fracint expint frac expintdigitdigit1-9 digits- digit- digit1-9 digitsfrac. digitsexpe digitsdigitsdigitdigit digitsee e+ e-E E+ E-
全局變量JSON
用作生成和解析帶有 JSON 數據的字符串的函數的命名空間。
JSON.stringify(value, replacer?, space?)
JSON.stringify(value, replacer?, space?)
將 JavaScript 值value
轉換為 JSON 格式的字符串。它有兩個可選參數。
可選參數replacer
用于在對其進行字符串化之前更改value
。它可以是:
-
一個節(jié)點訪問者(參見通過節(jié)點訪問者轉換數據)在將其字符串化之前轉換值樹。例如:
function replacer(key, value) {if (typeof value === 'number') {value = 2 * value;}return value; }
使用 replacer:
> JSON.stringify({ a: 5, b: [ 2, 8 ] }, replacer)'{"a":10,"b":[4,16]}'```+ 隱藏所有不在列表中的屬性鍵(非數組對象的屬性)的屬性白名單。例如:```js> JSON.stringify({foo: 1, bar: {foo: 1, bar: 1}}, ['bar'])'{"bar":{"bar":1}}'```白名單對數組沒有影響:```js> JSON.stringify(['a', 'b'], ['0'])'["a","b"]'```可選參數`space`影響輸出的格式。如果沒有這個參數,`stringify`的結果將是單行文本:```js
> console.log(JSON.stringify({a: 0, b: ['\n']}))
{"a":0,"b":["\n"]}
使用它,可以插入換行符,并且通過數組和對象的每個嵌套級別增加縮進。有兩種指定縮進方式的方法:
一個數字
將數字乘以縮進級別并將行縮進為相同數量的空格。小于 0 的數字被解釋為 0;大于 10 的數字被解釋為 10:
> console.log(JSON.stringify({a: 0, b: ['\n']}, null, 2))
{"a": 0,"b": ["\n"]
}
一個字符串
要縮進,重復給定的字符串以表示每個縮進級別。只使用字符串的前 10 個字符:
> console.log(JSON.stringify({a: 0, b: ['\n']}, null, '|--'))
{
|--"a": 0,
|--"b": [
|--|--"\n"
|--]
}
因此,以下對JSON.stringify()
的調用會將對象打印為一個格式良好的樹:
JSON.stringify(data, null, 4)
JSON.stringify()忽略的數據
在對象中,JSON.stringify()
只考慮可枚舉的自有屬性(參見屬性特性和屬性描述符)。以下示例演示了忽略了不可枚舉的自有屬性obj.foo
:
> var obj = Object.defineProperty({}, 'foo', { enumerable: false, value: 7 });
> Object.getOwnPropertyNames(obj)
[ 'foo' ]
> obj.foo
7
> JSON.stringify(obj)
'{}'
JSON.stringify()
處理不受 JSON 支持的值(例如函數和undefined
)的方式取決于它們遇到的位置。不支持的值本身導致stringify()
返回undefined
而不是字符串:
> JSON.stringify(function () {})
undefined
其值不受支持的屬性將被簡單地忽略:
> JSON.stringify({ foo: function () {} })
'{}'
數組中不支持的值將被字符串化為null
:
> JSON.stringify([ function () {} ])
'[null]'
toJSON()方法
如果JSON.stringify()
遇到具有toJSON
方法的對象,則使用該方法獲取要字符串化的值。例如:
> JSON.stringify({ toJSON: function () { return 'Cool' } })
'"Cool"'
日期已經有一個產生 ISO 8601 日期字符串的toJSON
方法:
> JSON.stringify(new Date('2011-07-29'))
'"2011-07-28T22:00:00.000Z"'
toJSON
方法的完整簽名如下:
function (key)
key
參數允許您根據上下文以不同方式進行字符串化。它始終是一個字符串,并指示在父對象中找到您的對象的位置:
根位置
空字符串
屬性值
屬性鍵
數組元素
元素的索引作為字符串
我將通過以下對象演示toJSON()
:
var obj = {toJSON: function (key) {// Use JSON.stringify for nicer-looking outputconsole.log(JSON.stringify(key));return 0;}
};
如果使用JSON.stringify()
,則每次出現obj
都會被替換為0
。通知toJSON()
方法在屬性鍵'foo'
和數組索引 0 處遇到了obj
:
> JSON.stringify({ foo: obj, bar: [ obj ]})
"foo"
"0"
'{"foo":0,"bar":[0]}'
內置的toJSON()
方法如下:
-
Boolean.prototype.toJSON()
-
Number.prototype.toJSON()
-
String.prototype.toJSON()
-
Date.prototype.toJSON()
JSON.parse(text, reviver?)
JSON.parse(text, reviver?)
解析text
中的 JSON 數據并返回 JavaScript 值。以下是一些示例:
> JSON.parse("'String'") // illegal quotes
SyntaxError: Unexpected token ILLEGAL
> JSON.parse('"String"')
'String'
> JSON.parse('123')
123
> JSON.parse('[1, 2, 3]')
[ 1, 2, 3 ]
> JSON.parse('{ "hello": 123, "world": 456 }')
{ hello: 123, world: 456 }
可選參數reviver
是一個節(jié)點訪問者(參見通過節(jié)點訪問者轉換數據),可用于轉換解析后的數據。在此示例中,我們將日期字符串轉換為日期對象:
function dateReviver(key, value) {if (typeof value === 'string') {var x = Date.parse(value);if (!isNaN(x)) { // valid date string?return new Date(x);}}return value;
}
以下是交互:
> var str = '{ "name": "John", "birth": "2011-07-28T22:00:00.000Z" }';
> JSON.parse(str, dateReviver)
{ name: 'John', birth: Thu, 28 Jul 2011 22:00:00 GMT }
通過節(jié)點訪問者轉換數據
JSON.stringify()
和JSON.parse()
都允許您通過傳遞函數來轉換 JavaScript 數據:
-
JSON.stringify()
允許您在將其轉換為 JSON 之前更改 JavaScript 數據。 -
JSON.parse()
解析 JSON,然后讓您對生成的 JavaScript 數據進行后處理。
JavaScript 數據是一個樹,其復合節(jié)點是數組和對象,其葉子是原始值(布爾值,數字,字符串,null
)。讓我們將傳遞的轉換函數稱為節(jié)點訪問者。這些方法遍歷樹并為每個節(jié)點調用訪問者。然后可以選擇替換或刪除節(jié)點。節(jié)點訪問者的簽名如下:
function nodeVisitor(key, value)
參數是:
this
當前節(jié)點的父節(jié)點。
key
當前節(jié)點位于其父節(jié)點內的鍵。key
總是一個字符串。
值
當前節(jié)點。
根節(jié)點 root
沒有父節(jié)點。當訪問 root
時,為其創(chuàng)建了一個偽父節(jié)點,并且參數具有以下值:
-
this
是{ '': root }
。 -
key
是''
。 -
value
是root
。
節(jié)點訪問者有三種返回值的選項:
-
返回
value
,然后不執(zhí)行任何更改。 -
返回不同的值。然后當前節(jié)點被替換。
-
返回
undefined
。然后移除節(jié)點。
以下是節(jié)點訪問者的示例。它記錄了傳遞給它的值。
function nodeVisitor(key, value) {console.log([// Use JSON.stringify for nicer-looking outputJSON.stringify(this), // parentJSON.stringify(key),JSON.stringify(value)].join(' # '));return value; // don't change node
}
讓我們使用此函數來檢查 JSON 方法如何迭代 JavaScript 數據。
JSON.stringify()
特殊的根節(jié)點首先出現在前綴迭代中(父節(jié)點在子節(jié)點之前)。訪問的第一個節(jié)點始終是偽根。在每次調用后顯示的最后一行是 stringify()
返回的字符串:
> JSON.stringify(['a','b'], nodeVisitor)
{"":["a","b"]} # "" # ["a","b"]
["a","b"] # "0" # "a"
["a","b"] # "1" # "b"
'["a","b"]'> JSON.stringify({a:1, b:2}, nodeVisitor)
{"":{"a":1,"b":2}} # "" # {"a":1,"b":2}
{"a":1,"b":2} # "a" # 1
{"a":1,"b":2} # "b" # 2
'{"a":1,"b":2}'> JSON.stringify('abc', nodeVisitor)
{"":"abc"} # "" # "abc"
'"abc"'
JSON.parse()
首先是葉子節(jié)點,在后綴迭代中(子節(jié)點在父節(jié)點之前)。訪問的最后一個節(jié)點始終是偽根。在每次調用后顯示的最后一行是 parse()
返回的 JavaScript 值:
> JSON.parse('["a","b"]', nodeVisitor)
["a","b"] # "0" # "a"
["a","b"] # "1" # "b"
{"":["a","b"]} # "" # ["a","b"]
[ 'a', 'b' ]> JSON.parse('{"a":1, "b":2}', nodeVisitor)
{"a":1,"b":2} # "a" # 1
{"a":1,"b":2} # "b" # 2
{"":{"a":1,"b":2}} # "" # {"a":1,"b":2}
{ a: 1, b: 2 }> JSON.parse('"hello"', nodeVisitor)
{"":"hello"} # "" # "hello"
'hello'
第二十三章:標準全局變量
原文:23. Standard Global Variables
譯者:飛龍
協議:CC BY-NC-SA 4.0
本章是 ECMAScript 規(guī)范標準化的全局變量的參考。Web 瀏覽器有更多全局變量,這些變量在 MDN 上列出。所有全局變量都是全局對象的(自有或繼承的)屬性(在瀏覽器中是 window
;參見 全局對象)。
構造函數
有關以下構造函數的詳細信息,請參見括號中指示的部分:
-
Array
([數組構造函數](ch18.html#array_constructor “數組構造函數”)) -
Boolean
([原始值的包裝對象](ch08.html#wrapper_objects “原始值的包裝對象”)) -
Date
([日期構造函數](ch20.html#date_constructors “日期構造函數”)) -
Function
([使用 new Function() 評估代碼](ch23.html#function_constructor “使用 new Function() 評估代碼”)) -
Number
([原始值的包裝對象](ch08.html#wrapper_objects “原始值的包裝對象”)) -
對象
([將任何值轉換為對象](ch17_split_000.html#toobject “將任何值轉換為對象”)) -
RegExp
([創(chuàng)建正則表達式](ch19.html#creating_regexps “創(chuàng)建正則表達式”)) -
String
([原始值的包裝對象](ch08.html#wrapper_objects “原始值的包裝對象”))
錯誤構造函數
有關這些構造函數的詳細信息,請參見 [錯誤構造函數](ch14.html#error_constructors “錯誤構造函數”):
-
Error
-
EvalError
-
RangeError
-
ReferenceError
-
SyntaxError
-
TypeError
-
URIError
非構造函數
有幾個全局函數不是構造函數。它們在本節(jié)中列出。
編碼和解碼文本
以下函數處理 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
也不會對其進行解碼,即使它們已經被正確編碼:
> 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()
。
對數字進行分類和解析
以下方法有助于對數字進行分類和解析:
-
isFinite(number)
(檢查是否為無窮大) -
isNaN(value)
(陷阱:檢查值是否為 NaN) -
parseFloat(string)
(parseFloat()) -
parseInt(string, radix)
(通過 parseInt()獲取整數)
通過 eval()和 new Function()動態(tài)評估 JavaScript 代碼
本節(jié)將介紹如何在 JavaScript 中動態(tài)評估代碼。
使用 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()
,您確實應該使用嚴格模式(參見嚴格模式)。在松散模式下,評估的代碼可以在周圍范圍內創(chuàng)建局部變量:
function sloppyFunc() {eval('var foo = 123'); // added to the scope of sloppyFuncconsole.log(foo); // 123
}
在嚴格模式下無法發(fā)生:
function strictFunc() {'use strict';eval('var foo = 123');console.log(foo); // ReferenceError: foo is not defined
}
然而,即使在嚴格模式下,評估的代碼仍然可以讀取和寫入周圍范圍內的變量。要防止這種訪問,您需要間接調用eval()
。
間接 eval()在全局范圍內進行評估
有兩種調用eval()
的方法:
-
直接。通過直接調用名稱為“eval”的函數。
-
間接調用。以其他方式(通過
call()
,作為window
的方法,通過在不同名稱下存儲它并在那里調用等)。
正如我們已經看到的,直接eval()
在當前范圍內執(zhí)行代碼:
var x = 'global';function directEval() {'use strict';var x = 'local';console.log(eval('x')); // local
}
相反,間接eval()
在全局范圍內執(zhí)行它:
var x = 'global';function indirectEval() {'use strict';var x = 'local';// Don’t call eval directlyconsole.log(eval.call(null, 'x')); // globalconsole.log(window.eval('x')); // globalconsole.log((1, eval)('x')); // global (1)// Change the name of evalvar xeval = eval;console.log(xeval('x')); // global// Turn eval into a methodvar obj = { eval: eval };console.log(obj.eval('x')); // global
}
(1)的解釋:當您通過名稱引用變量時,初始結果是所謂的引用,一個具有兩個主要字段的數據結構:
-
base
指向環(huán)境,即變量值存儲的數據結構。 -
referencedName
是變量的名稱。
在eval()
函數調用期間,函數調用運算符(括號)遇到對eval
的引用,并且可以確定要調用的函數的名稱。因此,這樣的函數調用觸發(fā)了直接的eval()
。但是,您可以通過不給出調用運算符的引用來強制間接eval()
。這是通過在應用運算符之前檢索引用的值來實現的。逗號運算符在第(1)行為我們執(zhí)行此操作。此運算符評估第一個操作數并返回評估第二個操作數的結果。評估始終產生值,這意味著引用被解析并丟失了函數名稱。
間接評估的代碼總是松散的。這是代碼獨立于其當前環(huán)境進行評估的結果:
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)
它創(chuàng)建一個函數,其零個或多個參數的名稱為param1
,parem2
等,其主體為funcBody
;也就是說,創(chuàng)建的函數如下所示:
function (?param1?, ..., ?paramN?) {?funcBody?
}
讓我們使用new Function()
創(chuàng)建一個函數f
,它返回其參數的總和:
> var f = new Function('x', 'y', 'return x+y');
> f(3, 4)
7
類似于間接eval()
,new Function()
創(chuàng)建其作用域為全局的函數:1?
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 modevar st = new Function('"use strict"; return this');console.log(st() === undefined); // true, strict mode
}
eval()與 new Function()
通常,最好使用new Function()
而不是eval()
來評估代碼:函數參數為評估的代碼提供了清晰的接口,而且你不需要間接eval()
的略顯笨拙的語法來確保評估的代碼只能訪問全局變量(除了它自己的變量)。
最佳實踐
你不應該使用eval()
和new Function()
。動態(tài)評估代碼很慢,而且存在潛在的安全風險。它還會阻止大多數使用靜態(tài)分析的工具(如 IDE)考慮代碼。
通常有更好的替代方案。例如,Brendan Eich 最近在推特上發(fā)推文指出了程序員們使用的反模式,他們想要訪問存儲在變量propName
中的屬性:
var value = eval('obj.'+propName);
這個想法是有道理的:點運算符只支持固定的,靜態(tài)提供的屬性鍵。在這種情況下,屬性鍵只在運行時知道,這就是為什么需要eval()
來使用該運算符。幸運的是,JavaScript 還有方括號運算符,它接受動態(tài)屬性鍵。因此,以下是前面代碼的更好版本:
var value = obj[propName];
你也不應該使用eval()
或new Function()
來解析 JSON 數據。這是不安全的。要么依賴 ECMAScript 5 對 JSON 的內置支持(參見第二十二章),要么使用一個庫。
合法的用例
eval()
和new Function()
有一些合法的,盡管是高級的用例:帶有函數的配置數據(JSON 不允許),模板庫,解釋器,命令行和模塊系統(tǒng)。
結論
這是 JavaScript 動態(tài)評估代碼的一個相對高級的概述。如果你想深入了解,可以查看 kangax 的文章“全局 eval。有哪些選項?”。
控制臺 API
在大多數 JavaScript 引擎中,有一個全局對象console
,其中包含用于記錄和調試的方法。該對象不是語言本身的一部分,但已成為事實上的標準。由于它們的主要目的是調試,console
方法在開發(fā)過程中最常用,而在部署的代碼中很少使用。
本節(jié)概述了控制臺 API。它記錄了 Chrome 32、Firebug 1.12、Firefox 25、Internet Explorer 11、Node.js 0.10.22 和 Safari 7.0 的現狀。
控制臺 API 在各種引擎之間的標準化程度如何?
控制臺 API 的實現差異很大,而且不斷變化。如果你想要權威的文檔,你有兩個選擇。首先,你可以查看 API 的標準概述:
-
Firebug 首先實現了控制臺 API,其在其維基中的文檔是目前最接近標準的東西。
-
此外,Brian Kardell 和 Paul Irish 正在制定API 規(guī)范,這應該會導致更一致的行為。
其次,你可以查看各種引擎的文檔:
-
Chrome
-
Firebug
-
Firefox
-
Internet Explorer
-
Node.js
-
Safari
警告
在 Internet Explorer 9 中存在一個錯誤。在該瀏覽器中,只有開發(fā)者工具至少打開過一次,console
對象才存在。這意味著如果在工具打開之前引用console
,你會得到一個ReferenceError
。作為一種解決方法,你可以檢查console
是否存在,如果不存在則創(chuàng)建一個虛擬實現。
簡單的日志記錄
控制臺 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?)
如果expr
為false
,則將obj
記錄到控制臺并拋出異常。如果為true
,則什么也不做。
console.count(label?)
計算帶有此語句的行被執(zhí)行的次數。
在以下表中指出了各種平臺的支持:
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.groupCollapsed()
打開的組。
console.table(data, columns?)
將數組打印為表格,每行一個元素??蛇x參數columns
指定在列中顯示哪些屬性/數組索引。如果缺少該參數,則所有屬性鍵都將用作表格列。缺少的屬性和數組元素顯示為列中的undefined
:
var persons = [{ firstName: 'Jane', lastName: 'Bond' },{ firstName: 'Lars', lastName: 'Croft', age: 72 }
];
// Equivalent:
console.table(persons);
console.table(persons, ['firstName', 'lastName', 'age']);
結果表如下:
(索引) | 名字 | 姓氏 | 年齡 |
---|---|---|---|
0 | “Jane” | “Bond” | undefined |
1 | “Lars” | “Croft” | 72 |
在以下表中指出了各種平臺的支持:
Chrome | Firebug | Firefox | IE | Node.js | Safari | |
---|---|---|---|---|---|---|
dir | ? | ? | ? | ? | ? | ? |
dirxml | ? | ? | ? | ? | ||
group | ? | ? | ? | ? | ? | |
groupCollapsed | ? | ? | ? | ? | ? | |
groupEnd | ? | ? | ? | ? | ? | |
table | ? | ? |
分析和計時
控制臺 API 包括以下用于分析和計時的方法:
控制臺.標記時間線(標簽)
[僅限 Safari]
與console.timeStamp
相同。
控制臺.性能(標題?)
打開分析。可選的title
用于分析報告。
控制臺.分析結束()
停止分析并打印分析報告。
控制臺.時間(標簽)
啟動標簽為label
的計時器。
控制臺.時間結束(標簽)
停止標簽為label
的計時器并打印自啟動以來經過的時間。
控制臺.時間戳(標簽?)
記錄具有給定label
的時間戳??梢杂涗浀娇刂婆_或時間軸。
以下表格顯示了各種平臺上的支持:
Chrome | Firebug | Firefox | IE | Node.js | Safari | |
---|---|---|---|---|---|---|
markTimeline | ? | |||||
profile | ? | ? | (devtools) | ? | ? | |
profileEnd | ? | ? | (devtools) | ? | ? | |
time | ? | ? | ? | ? | ? | ? |
timeEnd | ? | ? | ? | ? | ? | ? |
timeStamp | ? | ? |
markTimeline
以斜體排版,因為它僅在單個平臺上受支持。 (devtools)表示必須打開開發(fā)人員工具才能使該方法起作用。1?
命名空間和特殊值
以下全局變量用作函數的命名空間。有關詳細信息,請參閱括號中指示的材料:
JSON
JSON API 功能([第二十二章](ch22.html “第二十二章.JSON”))
數學
數學 API 功能([第二十一章](ch21.html “第二十一章.數學”))
對象
元編程功能([對象操作小抄:使用對象](ch17_split_001.html#oop_cheat_sheet “對象操作小抄:使用對象”))
以下全局變量包含特殊值。有關更多信息,請查看括號中指示的材料:
未定義
表示某物不存在的值([未定義和 null](ch08.html#undefined_null “未定義和 null”):
> ({}.foo) === undefined
true
NaN
一個表示某物是“非數字”([NaN](ch11.html#nan “NaN”)的值:
> 1 / 'abc'
NaN
無窮大
表示數值無窮大∞的值([無窮大](ch11.html#infinity “無窮大”):
> 1 / 0
Infinity
1? Mariusz Nowak(@medikoo)告訴我,由Function
評估的代碼默認情況下在任何地方都是松散的。
1? 感謝 Matthias Reuter(@gweax)和 Philipp Kyeck(@pkyeck)對本節(jié)的貢獻。
第二十四章: Unicode 和 JavaScript
原文:24. Unicode and JavaScript
譯者:飛龍
協議:CC BY-NC-SA 4.0
本章是對 Unicode 及其在 JavaScript 中的處理的簡要介紹。
Unicode 歷史
Unicode 始于 1987 年,由 Joe Becker(施樂),Lee Collins(蘋果)和 Mark Davis(蘋果)發(fā)起。其想法是創(chuàng)建一個通用字符集,因為當時對于編碼純文本存在許多不兼容的標準:許多變體的 8 位 ASCII,Big Five(繁體中文),GB 2312(簡體中文)等。在 Unicode 之前,沒有多語言純文本的標準,但有豐富的文本系統(tǒng)(例如蘋果的 WorldScript),允許您組合多個編碼。
第一份 Unicode 草案提案于 1988 年發(fā)布。此后繼續(xù)工作并擴大工作組。Unicode 聯盟于 1991 年 1 月 3 日成立:
Unicode 聯盟是一家致力于開發(fā)、維護和推廣軟件國際化標準和數據的非營利性公司,特別是 Unicode 標準[…]
Unicode 1.0 標準的第一卷于 1991 年 10 月出版,第二卷于 1992 年 6 月出版。
重要的 Unicode 概念
字符的概念可能看起來很簡單,但它有許多方面。這就是為什么 Unicode 是一個如此復雜的標準。以下是重要的基本概念:
字符和字形
這兩個術語的意思相似。字符是數字實體,而字形是書面語言的原子單位(字母、印刷連字、中文字符、標點符號等)。程序員以字符為思考單位,而用戶以字形為思考單位。有時需要使用多個字符來表示單個字形。例如,我們可以通過組合字符o和字符^(抑揚符)來產生單個字形?。
字形
這是一種顯示字形的具體方式。有時,相同的字形在不同的上下文或其他因素下顯示方式不同。例如,字形f和i可以呈現為字形f和字形i,通過連字字形連接,或者沒有連字。
代碼點
Unicode 通過稱為代碼點的數字來表示它支持的字符。代碼點的十六進制范圍是 0x0 到 0x10FFFF(17 倍 16 位)。
代碼單元
為了存儲或傳輸代碼點,我們將它們編碼為代碼單元,這是具有固定長度的數據片段。長度以位為單位,并由編碼方案確定,Unicode 有幾種編碼方案,例如 UTF-8 和 UTF-16。名稱中的數字表示代碼單元的長度,以位為單位。如果一個代碼點太大而無法適應單個代碼單元,它必須被分解為多個單元;也就是說,表示單個代碼點所需的代碼單元數量可能會有所不同。
BOM(字節(jié)順序標記)
如果一個代碼單元大于一個字節(jié),字節(jié)順序很重要。BOM 是文本開頭的一個偽字符(可能被編碼為多個代碼單元),指示代碼單元是大端(最重要的字節(jié)在前)還是小端(最不重要的字節(jié)在前)。沒有 BOM 的文本的默認值是大端。BOM 還指示所使用的編碼;對于 UTF-8、UTF-16 等編碼是不同的。此外,如果 Web 瀏覽器沒有關于文本編碼的其他信息,它還可以作為 Unicode 的標記。然而,由于幾個原因,BOM 并不經常使用:
-
UTF-8 是迄今為止最流行的 Unicode 編碼,不需要 BOM,因為只有一種字節(jié)排序方式。
-
幾種字符編碼規(guī)定了固定的字節(jié)順序。那么就不應該使用 BOM。例如 UTF-16BE(UTF-16 大端)、UTF-16LE、UTF-32BE 和 UTF-32LE。這是處理字節(jié)順序的更安全的方式,因為元數據和數據保持分開,不會混淆。
規(guī)范化
有時相同的字形可以用幾種方式表示。例如,字形?可以表示為單個代碼點,也可以表示為一個o后跟一個組合字符¨(分音符,雙點)。規(guī)范化是將文本轉換為規(guī)范表示的過程;等效的代碼點和代碼點序列都被轉換為相同的代碼點(或代碼點序列)。這對于文本處理(例如搜索文本)很有用。Unicode 規(guī)定了幾種規(guī)范化。
字符屬性
規(guī)范指定了規(guī)范的幾個屬性,其中一些列在這里:
-
名稱。一個由大寫字母 A-Z,數字 0-9,連字符(-)和<空格>組成的英文名稱。兩個例子:
-
“λ”的名稱是“希臘小寫字母λ”。
-
“!”的名稱是“感嘆號”。
-
一般類別。將字符分成字母、大寫字母、數字和標點等類別。
-
年齡。該字符是在哪個版本的 Unicode 中引入的(1.0、1.1、2.0 等)?
-
已棄用。是否不鼓勵使用該字符?
-
以及更多。
代碼點
代碼點的范圍最初是 16 位。隨著 Unicode 版本 2.0(1996 年 7 月)的擴展,它現在被分成了 17 個平面,編號從 0 到 16。每個平面包括 16 位(十六進制表示法:0x0000–0xFFFF)。因此,在接下來的十六進制范圍中,四個底部以外的數字包含了平面的編號。
-
第 0 平面,基本多文種平面(BMP):0x0000–0xFFFF
-
第 1 平面,補充多語種平面(SMP):0x10000–0x1FFFF
-
第 2 平面,補充表意文字平面(SIP):0x20000–0x2FFFF
-
第 3–13 平面,未分配
-
第 14 平面,補充特殊用途平面(SSP):0xE0000–0xEFFFF
-
第 15–16 平面,補充專用區(qū)域(S PUA A/B):0x0F0000–0x10FFFF
第 1–16 平面稱為補充平面或星際平面。
Unicode 編碼
UTF-32(Unicode 轉換格式 32)是一種具有 32 位代碼單元的格式。任何代碼點都可以由單個代碼單元編碼,使得這是唯一的固定長度編碼;對于其他編碼,編碼一個點所需的單元數量是變化的。
UTF-16是一種具有 16 位代碼單元的格式,需要一個到兩個單元來表示一個代碼點。BMP 代碼點可以由單個代碼單元表示。高代碼點是 20 位(16 乘以 16 位),在減去 0x10000(BMP 的范圍)后。這些位被編碼為兩個代碼單元(所謂的代理對):
領先代理
最重要的 10 位:存儲在范圍 0xD800–0xDBFF 中。也稱為高代理代碼單元。
尾隨代理
最不重要的 10 位:存儲在范圍 0xDC00–0xDFFF 中。也稱為低代理代碼單元。
以下表格(改編自 Unicode 標準 6.2.0,表 3-5)可視化了位的分布:
代碼點 | UTF-16 代碼單元 |
---|---|
xxxxxxxxxxxxxxxx(16 位) | xxxxxxxxxxxxxxxx |
pppppxxxxxxyyyyyyyyyy(21 位=5+6+10 位) | 110110qqqqxxxxxx 110111yyyyyyyyyy(qqqq = ppppp ? 1) |
為了啟用這種編碼方案,BMP 有一個未使用的代碼點范圍為 0xD800–0xDFFF 的空隙。因此,領先代理、尾隨代理和 BMP 代碼點的范圍是不相交的,使得在面對錯誤時解碼更加健壯。以下函數將代碼點編碼為 UTF-16(稍后我們將看到一個使用它的示例):
function toUTF16(codePoint) {var TEN_BITS = parseInt('1111111111', 2);function u(codeUnit) {return '\\u'+codeUnit.toString(16).toUpperCase();}if (codePoint <= 0xFFFF) {return u(codePoint);}codePoint -= 0x10000;// Shift right to get to most significant 10 bitsvar leadingSurrogate = 0xD800 | (codePoint >> 10);// Mask to get least significant 10 bitsvar trailingSurrogate = 0xDC00 | (codePoint & TEN_BITS);return u(leadingSurrogate) + u(trailingSurrogate);
}
UCS-2,一種已棄用的格式,使用 16 位代碼單元來表示(僅!)BMP 的代碼點。當 Unicode 代碼點的范圍擴展到 16 位之外時,UTF-16 取代了 UCS-2。
UTF-8具有 8 位代碼單元。它在傳統(tǒng) ASCII 編碼和 Unicode 之間架起了一座橋梁。ASCII 只有 128 個字符,其編號與前 128 個 Unicode 代碼點相同。UTF-8 是向后兼容的,因為所有 ASCII 代碼都是有效的代碼單元。換句話說,在范圍 0–127 的單個代碼單元中編碼了相同范圍內的單個代碼點。這些代碼單元的最高位為零。另一方面,如果最高位為 1,則會跟隨更多的單元,以為更高的代碼點提供額外的位。這導致了以下編碼方案:
-
0000–007F:0xxxxxxx(7 位,存儲在 1 字節(jié)中)
-
0080–07FF:110xxxxx,10xxxxxx(5+6 位=11 位,存儲在 2 字節(jié)中)
-
0800–FFFF:1110xxxx,10xxxxxx,10xxxxxx(4+6+6 位=16 位,存儲在 3 字節(jié)中)
-
10000–1FFFFF:11110xxx,10xxxxxx,10xxxxxx,10xxxxxx(3+6+6+6 位=21 位,存儲在 4 字節(jié)中)。最高代碼點是 10FFFF,因此 UTF-8 有一些額外的空間。
如果最高位不為 0,則零之前的 1 的數量表示序列中有多少個代碼單元。初始單元之后的所有單元都具有位前綴 10。因此,初始代碼單元和后續(xù)代碼單元的范圍是不相交的,這有助于從編碼錯誤中恢復。
UTF-8 已成為最流行的 Unicode 格式。最初,它之所以受歡迎,是因為它與 ASCII 的向后兼容性。后來,它因其在操作系統(tǒng)、編程環(huán)境和應用程序中的廣泛和一致的支持而受到青睞。
JavaScript 源代碼和 Unicode
JavaScript 處理 Unicode 源代碼有兩種方式:內部(在解析期間)和外部(在加載文件時)。
內部源代碼
在內部,JavaScript 源代碼被視為一系列 UTF-16 代碼單元。根據第 6 節(jié)的 EMCAScript 規(guī)范:
ECMAScript 源文本以 Unicode 字符編碼的形式表示,版本為 3.0 或更高。[…] ECMAScript 源文本被假定為本規(guī)范的目的是一系列 16 位代碼單元。[…] 如果實際源文本以除 16 位代碼單元以外的形式編碼,必須處理為首先轉換為 UTF-16。
在標識符、字符串文字和正則表達式文字中,任何代碼單元也可以通過 Unicode 轉義序列\uHHHH
來表示,其中HHHH
是四個十六進制數字。例如:
> var f\u006F\u006F = 'abc';
> foo
'abc'> var λ = 123;
> \u03BB
123
這意味著您可以在源代碼中使用 Unicode 字符的文字和變量名,而不會離開 ASCII 范圍。
在字符串文字中,還有一種額外的轉義可用:用兩位十六進制數字表示的十六進制轉義序列,表示范圍在 0x00-0xFF 的代碼單元。例如:
> '\xF6' === '?'
true
> '\xF6' === '\u00F6'
true
外部源代碼
雖然內部使用 UTF-16,但 JavaScript 源代碼通常不以該格式存儲。當 Web 瀏覽器通過<script>
標簽加載源文件時,它會確定編碼如下:
-
如果文件以 BOM 開頭,則編碼是 UTF 變體,取決于使用的 BOM。
-
否則,如果文件是通過 HTTP(S)加載的,那么
Content-Type
頭可以通過charset
參數指定編碼。例如:Content-Type: application/javascript; charset=utf-8
提示
JavaScript 文件的正確媒體類型(以前稱為MIME 類型)是application/javascript
。但是,較舊的瀏覽器(例如 Internet Explorer 8 及更早版本)最可靠地使用text/javascript
。不幸的是,<script>
標簽的type
屬性的默認值是text/javascript
。至少對于 JavaScript,您可以省略該屬性;包含它沒有好處。
-
否則,如果
<script>
標簽具有charset
屬性,則將使用該編碼。即使屬性type
包含有效的媒體類型,該類型也不得具有參數charset
(就像前述的Content-Type
頭)。這確保了charset
和type
的值不會沖突。 -
否則,使用包含
<script>
標簽的文檔的編碼。例如,這是 HTML5 文檔的開頭,其中<meta>
標簽聲明文檔編碼為 UTF-8:<!doctype html> <html> <head><meta charset="UTF-8"> ...
強烈建議您始終指定編碼。如果不指定,將使用特定于區(qū)域設置的默認編碼。換句話說,在不同國家,人們將以不同方式看待文件。只有最低的 7 位在各個區(qū)域設置中相對穩(wěn)定。
我的建議可以總結如下:
-
對于您自己的應用程序,您可以使用 Unicode。但必須將應用程序的 HTML 頁面的編碼指定為 UTF-8。
-
對于庫,最安全的做法是發(fā)布 ASCII(7 位)代碼。
一些縮小工具可以將具有超出 7 位的 Unicode 代碼點的源代碼轉換為“7 位干凈”的源代碼。它們通過用 Unicode 轉義替換非 ASCII 字符來實現。例如,以下調用UglifyJS將文件test.js翻譯為:
uglifyjs -b beautify=false,ascii-only=true test.js
文件test.js如下所示:
var σ = 'K?ln';
UglifyJS 的輸出如下:
var \u03c3="K\xf6ln";
考慮以下負面示例。有一段時間,庫 D3.js 以 UTF-8 發(fā)布。這導致了一個錯誤,因為當它從編碼不是 UTF-8 的頁面加載時,代碼包含了諸如以下語句:
var π = Math.PI, ε = 1e-6;
標識符π和ε沒有被正確解碼,也沒有被識別為有效的變量名。此外,一些超出 7 位的代碼點的字符串文字也沒有被正確解碼。作為一種解決方法,您可以通過向<script>
標簽添加適當的charset
屬性來加載代碼:
<script charset="utf-8" src="d3.js"></script>
JavaScript 字符串和 Unicode
JavaScript 字符串是一系列 UTF-16 代碼單元。根據 ECMAScript 規(guī)范,第 8.4 節(jié):
當一個字符串包含實際文本數據時,每個元素被認為是單個 UTF-16 代碼單元。
轉義序列
如前所述,您可以在字符串文字中使用 Unicode 轉義序列和十六進制轉義序列。例如,您可以通過將o與重音符(代碼點 0x0308)組合來產生字符?:
> console.log('o\u0308')
?
這適用于 JavaScript 命令行,例如 Web 瀏覽器控制臺和 Node.js REPL。您還可以將這種類型的字符串插入到 Web 頁面的 DOM 中。
通過轉義引用星際飛機字符
網絡上有許多不錯的 Unicode 符號表。看看 Tim Whitlock 的“Emoji Unicode Tables”,并對現代 Unicode 字體中有多少符號感到驚訝。表中的符號都不是圖像;它們都是字體字形。假設您想通過 JavaScript 顯示一個星際飛機中的 Unicode 字符(顯然,這樣做存在風險:并非所有字體都支持所有這些字符)。例如,考慮一頭奶牛,代碼點為 0x1F404:!。
您可以復制字符并直接粘貼到您的 Unicode 編碼 JavaScript 源代碼中:
JavaScript 引擎將解碼源代碼(通常為 UTF-8)并創(chuàng)建一個具有兩個 UTF-16 代碼單元的字符串?;蛘?#xff0c;您可以自己計算兩個代碼單元并使用 Unicode 轉義序列。有一些網絡應用程序可以執(zhí)行這種計算,例如:
-
UTF 轉換器
-
Mathias Bynens 的“JavaScript 轉義”
先前定義的函數toUTF16
也執(zhí)行了它:
> toUTF16(0x1F404)
'\\uD83D\\uDC04'
UTF-16 代理對(0xD83D,0xDC04)確實編碼了奶牛:
計數字符
如果字符串包含代理對(兩個編碼單元編碼一個代碼點),那么length
屬性不再計算圖形元素。它計算編碼單元:
這可以通過庫來修復,例如 Mathias Bynens 的Punycode.js,它與 Node.js 捆綁在一起:
> var puny = require('punycode');
> puny.ucs2.decode(str).length
1
Unicode 規(guī)范化
如果您想在字符串中搜索或比較它們,那么您需要進行規(guī)范化,例如通過庫unorm(由 Bjarke Walling)。
JavaScript 正則表達式和 Unicode
JavaScript 正則表達式中的 Unicode 支持(請參閱第十九章)非常有限。例如,沒有辦法匹配“大寫字母”等 Unicode 類別。
行終止符影響匹配。行終止符是下表中指定的四個字符之一:
代碼單元 | 名稱 | 字符轉義序列 |
---|---|---|
\u000A | 換行符 | \n |
\u000D | 回車 | \r |
\u2028 | 行分隔符 | |
\u2029 | 段落分隔符 |
以下正則表達式構造基于 Unicode:
-
\s \S
(空白,非空白)具有基于 Unicode 的定義:> /^\s$/.test('\uFEFF') true
-
.
(點)匹配所有代碼單元(不是代碼點!)除了行終止符。請參閱下一節(jié),了解如何匹配任何代碼點。 -
多行模式
/m
:在多行模式下,斷言^
匹配輸入的開頭和行終止符之后。斷言$
匹配行終止符之前和輸入的結尾。在非多行模式下,它們只在輸入的開頭或結尾匹配。
其他重要的字符類是基于 ASCII 而不是 Unicode 定義的:
-
\d \D
(數字,非數字):數字等同于[0-9]
。 -
\w \W
(單詞字符,非單詞字符):單詞字符等同于[A-Za-z0-9_]
。 -
\b \B
(在單詞邊界,單詞內):單詞是由單詞字符([A-Za-z0-9_]
)組成的序列。例如,在字符串'über'
中,字符類轉義\b
將字符b視為單詞的開始:> /\bb/.test('über') true
匹配任何代碼單元和任何代碼點
要匹配任何代碼單元,您可以使用[\s\S]
;請參見原子:通用。
要匹配任何代碼點,您需要使用:1?
([\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF])
前面的模式工作原理如下:
([BMP code point]|[leading surrogate][trailing surrogate])
由于所有這些范圍都是不相交的,該模式將正確匹配 UTF-16 字符串中的代碼點。
庫
一些庫可幫助處理 JavaScript 中的 Unicode:
-
Regenerate有助于生成像前面那樣的范圍,以匹配任何代碼單元。它旨在用作構建工具的一部分,但也可以動態(tài)工作,用于嘗試各種功能。
-
XRegExp是一個正則表達式庫,它有一個官方附加組件,可以通過以下三種構造之一匹配 Unicode 類別、腳本、塊和屬性:
\p{...} \p{^...} \P{...}
例如,\p{Letter}
匹配各種字母表中的字母,而\p{^Letter}
和\P{Letter}
都匹配所有其他代碼點。第三十章包含了對 XRegExp 的簡要概述。
- ECMAScript 國際化 API(請參見ECMAScript 國際化 API)提供了對 Unicode 的排序和搜索等功能。
推薦閱讀和章節(jié)來源
有關 Unicode 的更多信息,請參見以下內容:
-
維基百科上有幾篇關于Unicode及其術語的好文章。
-
Unicode.org,Unicode 聯盟的官方網站,以及其FAQ也是很好的資源。
-
Joel Spolsky 的介紹性文章“每個軟件開發(fā)人員絕對必須了解的有關 Unicode 和字符集的絕對最低限度(沒有借口!)”很有幫助。
有關 JavaScript 中的 Unicode 支持的信息,請參見:
-
Mathias Bynens 的文章“JavaScript 的內部字符編碼:UCS-2 還是 UTF-16?”
-
《JavaScript,正則表達式和 Unicode》由 Steven Levithan
致謝
以下人員為本章做出了貢獻:Mathias Bynens(@mathias),Anne van Kesteren(@annevk)和 Calvin Metcalf(@CWMma)。
1? 嚴格來說,任何Unicode 標量值。
第二十五章:ECMAScript 5 中的新功能
原文:25. New in ECMAScript 5
譯者:飛龍
協議:CC BY-NC-SA 4.0
本章列出了僅在 ECMAScript 5 中可用的功能。如果您必須使用舊版 JavaScript 引擎,您應該避免使用這些功能,或者通過庫啟用其中一些功能(稍后將進行描述)。請注意,通常情況下,本書假定您正在使用完全支持 ECMAScript 5 的現代引擎。
ECMAScript 5 規(guī)范包含了對其范圍的以下描述:
ECMAScript 的第五版(作為 ECMA-262 第 5 版發(fā)布)
對已成為瀏覽器實現中常見的語言規(guī)范的實際解釋進行了編碼
增加了對自第三版出版以來出現的新功能的支持。這些功能包括
訪問器屬性,
反射創(chuàng)建和檢查對象,
程序控制屬性屬性,
附加數組操作函數,
對 JSON 對象編碼格式的支持,以及 x
提供增強的錯誤檢查和程序安全性的嚴格模式。
新功能
ECMAScript 5 中包含的新功能如下:
嚴格模式(參見嚴格模式)
將以下行放在文件或函數的開頭可以打開所謂的嚴格模式,使 JavaScript 成為一個更干凈的語言,禁止一些功能,執(zhí)行更多檢查,并拋出更多異常:
'use strict';
訪問器(參見訪問器(Getter 和 Setter))
Getter 和 setter 允許您通過方法實現屬性的獲取和設置。例如,以下對象obj
包含屬性foo
的 getter:
> var obj = { get foo() { return 'abc' } };
> obj.foo
'abc'
語法更改
ECMAScript 5 包括以下語法更改:
保留字作為屬性鍵
您可以在點運算符之后使用保留字(例如new
和function
)并且在對象文字中作為非引用的屬性鍵:
> var obj = { new: 'abc' };
> obj.new
'abc'
合法的尾隨逗號
對象文字和數組文字中的尾隨逗號是合法的。
多行字符串文字
如果通過反斜杠轉義行尾,字符串文字可以跨多行。
標準庫中的新功能
ECMAScript 5 為 JavaScript 的標準庫帶來了幾個新增功能。本節(jié)按類別列出了它們。
元編程
獲取和設置原型(參見獲取和設置原型):
-
Object.create()
-
Object.getPrototypeOf()
通過屬性描述符管理屬性屬性(參見屬性描述符):
-
Object.defineProperty()
-
Object.defineProperties()
-
Object.create()
-
Object.getOwnPropertyDescriptor()
列出屬性(參見迭代和屬性檢測):
-
Object.keys()
-
Object.getOwnPropertyNames()
保護對象(參見保護對象):
-
Object.preventExtensions()
-
Object.isExtensible()
-
Object.seal()
-
Object.isSealed()
-
Object.freeze()
-
Object.isFrozen()
新的Function
方法(參見Function.prototype.bind(thisValue, arg1?, …, argN?)):
Function.prototype.bind()
新方法
字符串(參見第十二章):
-
新方法
String.prototype.trim()
-
通過方括號操作符
[...]
訪問字符
新的Array
方法(參見[Array Prototype Methods](ch18.html#array_prototype_methods “Array Prototype Methods”):
-
Array.isArray()
-
Array.prototype.every()
-
Array.prototype.filter()
-
Array.prototype.forEach()
-
Array.prototype.indexOf()
-
Array.prototype.lastIndexOf()
-
Array.prototype.map()
-
Array.prototype.reduce()
-
Array.prototype.some()
新的Date
方法(參見Date Prototype Methods):
-
Date.now()
-
Date.prototype.toISOString()
JSON
對 JSON 的支持(參見第二十二章):
-
JSON.parse()
(參見JSON.parse(text, reviver?)) -
JSON.stringify()
(參見JSON.stringify(value, replacer?, space?)) -
一些內置對象具有特殊的
toJSON()
方法: -
Boolean.prototype.toJSON()
-
Number.prototype.toJSON()
-
String.prototype.toJSON()
-
Date.prototype.toJSON()
與舊版瀏覽器一起工作的提示
如果您需要與舊版瀏覽器一起工作,以下資源將非常有用:
-
Juriy Zaytsev(“kangax”)的兼容性表顯示了各種瀏覽器的各個版本支持 ECMAScript 5 的程度。
-
es5-shim 將 ECMAScript 5 的大部分(但不是全部)功能帶到只支持 ECMAScript 3 的瀏覽器中。