第二十一章:数学
原文: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
三角函数
三角函数方法接受弧度作为角度并返回。以下函数向您展示了如何实现转换,如果需要的话:
从度到弧度:
代码语言:javascript复制function toRadians(degrees) {
return degrees / 180 * Math.PI;
}
以下是交互:
代码语言:javascript复制 > toRadians(180)
3.141592653589793
> toRadians(90)
1.5707963267948966
```
从弧度到度:
```js
function 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)`
返回商的反正切值![](inleq_2104.png)。
`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?, ...)
返回参数中的最大数:
代码语言:javascript复制> 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 值(解析)以及反之(字符串化)。
背景
本节解释了 JSON 是什么以及它是如何创建的。
数据格式
JSON 将数据存储为纯文本。它的语法是 JavaScript 表达式语法的子集。例如:
代码语言:javascript复制{
"first": "Jane",
"last": "Porter",
"married": true,
"born": 1890,
"friends": [ "Tarzan", "Cheeta" ]
}
JSON 使用 JavaScript 表达式中的以下结构:
复合
JSON 数据的对象和 JSON 数据的数组
原子
字符串、数字、布尔值和空值
它遵循以下规则:
- 字符串必须始终用双引号括起来;例如,像
'mystr'
这样的字符串字面量是非法的。 - 属性键必须用双引号括起来
历史
Douglas Crockford 于 2001 年发现了 JSON。他给它起了个名字,并在json.org
上发布了一个规范:
我发现了 JSON。我不主张发明 JSON,因为它已经存在于自然界中。我所做的是发现它,我给它起了名字,我描述了它的有用之处。我不主张自己是第一个发现它的人;我知道至少有其他人在我之前至少一年发现了它。我发现的最早的情况是,Netscape 有人在至少 1996 年就开始使用 JavaScript 数组文字进行数据通信,而这至少比我想到这个想法早了五年。
最初,Crockford 希望 JSON 有一个名字叫JavaScript 标记语言,但是 JSML 的首字母缩写已经被JSpeech 标记语言使用了。
JSON 规范已经被翻译成许多人类语言,现在有许多编程语言的库支持解析和生成 JSON。
语法
Douglas Crockford 创建了一张 JSON 名片,正面有一个徽标(参见图 22-1),背面有完整的语法(参见图 22-2)。这使得 JSON 的简单性在视觉上显而易见。
图 22-1:JSON 名片的正面显示了一个徽标(来源:Eric Miraglia)。
图 22-2:JSON 名片的背面包含完整的语法(来源:Eric Miraglia)。
语法可以转录如下:
代码语言:javascript复制object
{ }
{ members }
members
pair
pair , members
pair
string : value
array
[ ]
[ elements ]
elements
value
value , elements
value
string
number
object
array
true
false
null
string
""
" chars "
chars
char
char chars
char
any-Unicode-character-except-"-or--or-control-character
" \ / b f n r t
u four-hex-digits
number
int
int frac
int exp
int frac exp
int
digit
digit1-9 digits
- digit
- digit1-9 digits
frac
. digits
exp
e digits
digits
digit
digit digits
e
e e e-
E E E-
全局变量JSON
用作生成和解析带有 JSON 数据的字符串的函数的命名空间。
JSON.stringify(value, replacer?, space?)
JSON.stringify(value, replacer?, space?)
将 JavaScript 值value
转换为 JSON 格式的字符串。它有两个可选参数。
可选参数replacer
用于在对其进行字符串化之前更改value
。它可以是:
一个节点访问者(参见通过节点访问者转换数据)在将其字符串化之前转换值树。例如:
代码语言:javascript复制function replacer(key, value) {
if (typeof value === 'number') {
value = 2 * value;
}
return value;
}
使用 replacer:
代码语言:javascript复制 > 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:
代码语言:javascript复制> console.log(JSON.stringify({a: 0, b: ['n']}, null, 2))
{
"a": 0,
"b": [
"n"
]
}
一个字符串
要缩进,重复给定的字符串以表示每个缩进级别。只使用字符串的前 10 个字符:
代码语言:javascript复制> 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
其值不受支持的属性将被简单地忽略:
代码语言:javascript复制> 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 output
console.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
是一个节点访问者(参见通过节点访问者转换数据),可用于转换解析后的数据。在此示例中,我们将日期字符串转换为日期对象:
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;
}
以下是交互:
代码语言:javascript复制> 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 }
通过节点访问者转换数据
JSON.stringify()
和JSON.parse()
都允许您通过传递函数来转换 JavaScript 数据:
-
JSON.stringify()
允许您在将其转换为 JSON 之前更改 JavaScript 数据。 -
JSON.parse()
解析 JSON,然后让您对生成的 JavaScript 数据进行后处理。
JavaScript 数据是一个树,其复合节点是数组和对象,其叶子是原始值(布尔值,数字,字符串,null
)。让我们将传递的转换函数称为节点访问者。这些方法遍历树并为每个节点调用访问者。然后可以选择替换或删除节点。节点访问者的签名如下:
function nodeVisitor(key, value)
参数是:
this
当前节点的父节点。
key
当前节点位于其父节点内的键。key
总是一个字符串。
值
当前节点。
根节点 root
没有父节点。当访问 root
时,为其创建了一个伪父节点,并且参数具有以下值:
-
this
是{ '': root }
。 -
key
是''
。 -
value
是root
。
节点访问者有三种返回值的选项:
- 返回
value
,然后不执行任何更改。 - 返回不同的值。然后当前节点被替换。
- 返回
undefined
。然后移除节点。
以下是节点访问者的示例。它记录了传递给它的值。
代码语言:javascript复制function nodeVisitor(key, value) {
console.log([
// Use JSON.stringify for nicer-looking output
JSON.stringify(this), // parent
JSON.stringify(key),
JSON.stringify(value)
].join(' # '));
return value; // don't change node
}
让我们使用此函数来检查 JSON 方法如何迭代 JavaScript 数据。
JSON.stringify()
特殊的根节点首先出现在前缀迭代中(父节点在子节点之前)。访问的第一个节点始终是伪根。在每次调用后显示的最后一行是 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()
首先是叶子节点,在后缀迭代中(子节点在父节点之前)。访问的最后一个节点始终是伪根。在每次调用后显示的最后一行是 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 规范标准化的全局变量的参考。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
([创建正则表达式](ch19.html#creating_regexps “创建正则表达式”)) -
String
([原始值的包装对象](ch08.html#wrapper_objects “原始值的包装对象”))
错误构造函数
有关这些构造函数的详细信息,请参见 [错误构造函数](ch14.html#error_constructors “错误构造函数”):
-
Error
-
EvalError
-
RangeError
-
ReferenceError
-
SyntaxError
-
TypeError
-
URIError
非构造函数
有几个全局函数不是构造函数。它们在本节中列出。
编码和解码文本
以下函数处理 URI 编码和解码的几种方式:
encodeURI(uri)
在 uri
中对特殊字符进行百分比编码。特殊字符是除以下字符外的所有 Unicode 字符:
URI 字符: | ; , / ? : @ & = $ # |
---|---|
未编码: | a-z A-Z 0-9 - _ . ! ~ * ' ( ) |
例如:
代码语言:javascript复制> encodeURI('http://example.com/Für Elise/')
'http://example.com/Für Elise/'
encodeURIComponent(uriComponent)
在 uriComponent
中对所有字符进行百分比编码,除了:
| 未编码: | a-z A-Z 0-9 - _ . ! ~ * ' ( )
|
与 encodeURI
相反,URL 和文件名中有意义的字符也被编码了。因此,您可以使用此函数将任何文本转换为合法的文件名或 URL 路径段。例如:
> encodeURIComponent('http://example.com/Für Elise/')
'http://example.com/Für Elise/'
decodeURI(encodedURI)
解码由 encodeURI
生成的百分比编码的 URI:
> decodeURI('http://example.com/Für Elise/')
'http://example.com/Für Elise/'
encodeURI
不会对 URI 字符进行编码,decodeURI
也不会对其进行解码,即使它们已经被正确编码:
> decodeURI('/')
'/'
> decodeURIComponent('/')
'/'
decodeURIComponent(encodedURIComponent)
解码由 encodeURIComponent
生成的百分比编码的 URI 组件。与 decodeURI
相反,所有百分比编码的字符都被解码:
> decodeURIComponent('http://example.com/Für Elise/')
'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()动态评估 JavaScript 代码
本节将介绍如何在 JavaScript 中动态评估代码。
使用 eval()评估代码
函数调用:
代码语言:javascript复制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
}
在严格模式下无法发生:
代码语言:javascript复制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)行为我们执行此操作。此运算符评估第一个操作数并返回评估第二个操作数的结果。评估始终产生值,这意味着引用被解析并丢失了函数名称。
间接评估的代码总是松散的。这是代码独立于其当前环境进行评估的结果:
代码语言:javascript复制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)
它创建一个函数,其零个或多个参数的名称为param1
,parem2
等,其主体为funcBody
;也就是说,创建的函数如下所示:
function («param1», ..., «paramN») {
«funcBody»
}
让我们使用new Function()
创建一个函数f
,它返回其参数的总和:
> var f = new Function('x', 'y', 'return x y');
> f(3, 4)
7
类似于间接eval()
,new Function()
创建其作用域为全局的函数:¹⁶
var x = 'global';
function strictFunc() {
'use strict';
var x = 'local';
var f = new Function('return x');
console.log(f()); // global
}
这样的函数默认情况下也是松散的:
代码语言:javascript复制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()
通常,最好使用new Function()
而不是eval()
来评估代码:函数参数为评估的代码提供了清晰的接口,而且你不需要间接eval()
的略显笨拙的语法来确保评估的代码只能访问全局变量(除了它自己的变量)。
最佳实践
你不应该使用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 的内置支持(参见第二十二章),要么使用一个库。
合法的用例
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 的标准概述:
- Firebug 首先实现了控制台 API,其在其维基中的文档是目前最接近标准的东西。
- 此外,Brian Kardell 和 Paul Irish 正在制定API 规范,这应该会导致更一致的行为。
其次,你可以查看各种引擎的文档:
- Chrome
- Firebug
- Firefox
- Internet Explorer
- Node.js
- Safari
警告
在 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?)
如果expr
为false
,则将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.groupCollapsed()
打开的组。
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']);
结果表如下:
(索引) | 名字 | 姓氏 | 年龄 |
---|---|---|---|
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)表示必须打开开发人员工具才能使该方法起作用。¹⁷
命名空间和特殊值
以下全局变量用作函数的命名空间。有关详细信息,请参阅括号中指示的材料:
JSON
JSON API 功能([第二十二章](ch22.html “第二十二章.JSON”))
数学
数学 API 功能([第二十一章](ch21.html “第二十一章.数学”))
对象
元编程功能([对象操作小抄:使用对象](ch17_split_001.html#oop_cheat_sheet “对象操作小抄:使用对象”))
以下全局变量包含特殊值。有关更多信息,请查看括号中指示的材料:
未定义
表示某物不存在的值([未定义和 null](ch08.html#undefined_null “未定义和 null”):
代码语言:javascript复制> ({}.foo) === undefined
true
NaN
一个表示某物是“非数字”([NaN](ch11.html#nan “NaN”)的值:
代码语言:javascript复制> 1 / 'abc'
NaN
无穷大
表示数值无穷大∞的值([无穷大](ch11.html#infinity “无穷大”):
代码语言:javascript复制> 1 / 0
Infinity
¹⁶ Mariusz Nowak(@medikoo)告诉我,由Function
评估的代码默认情况下在任何地方都是松散的。
¹⁷ 感谢 Matthias Reuter(@gweax)和 Philipp Kyeck(@pkyeck)对本节的贡献。
第二十四章: Unicode 和 JavaScript
原文:24. Unicode and JavaScript 译者:飞龙 协议:CC BY-NC-SA 4.0
本章是对 Unicode 及其在 JavaScript 中的处理的简要介绍。
Unicode 历史
Unicode 始于 1987 年,由 Joe Becker(施乐),Lee Collins(苹果)和 Mark Davis(苹果)发起。其想法是创建一个通用字符集,因为当时对于编码纯文本存在许多不兼容的标准:许多变体的 8 位 ASCII,Big Five(繁体中文),GB 2312(简体中文)等。在 Unicode 之前,没有多语言纯文本的标准,但有丰富的文本系统(例如苹果的 WorldScript),允许您组合多个编码。
第一份 Unicode 草案提案于 1988 年发布。此后继续工作并扩大工作组。Unicode 联盟于 1991 年 1 月 3 日成立:
Unicode 联盟是一家致力于开发、维护和推广软件国际化标准和数据的非营利性公司,特别是 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(字节顺序标记)
如果一个代码单元大于一个字节,字节顺序很重要。BOM 是文本开头的一个伪字符(可能被编码为多个代码单元),指示代码单元是大端(最重要的字节在前)还是小端(最不重要的字节在前)。没有 BOM 的文本的默认值是大端。BOM 还指示所使用的编码;对于 UTF-8、UTF-16 等编码是不同的。此外,如果 Web 浏览器没有关于文本编码的其他信息,它还可以作为 Unicode 的标记。然而,由于几个原因,BOM 并不经常使用:
- UTF-8 是迄今为止最流行的 Unicode 编码,不需要 BOM,因为只有一种字节排序方式。
- 几种字符编码规定了固定的字节顺序。那么就不应该使用 BOM。例如 UTF-16BE(UTF-16 大端)、UTF-16LE、UTF-32BE 和 UTF-32LE。这是处理字节顺序的更安全的方式,因为元数据和数据保持分开,不会混淆。
规范化
有时相同的字形可以用几种方式表示。例如,字形ö可以表示为单个代码点,也可以表示为一个o后跟一个组合字符¨(分音符,双点)。规范化是将文本转换为规范表示的过程;等效的代码点和代码点序列都被转换为相同的代码点(或代码点序列)。这对于文本处理(例如搜索文本)很有用。Unicode 规定了几种规范化。
字符属性
规范指定了规范的几个属性,其中一些列在这里:
- 名称。一个由大写字母 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 平面,补充专用区域(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(稍后我们将看到一个使用它的示例):
代码语言:javascript复制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 bits
var leadingSurrogate = 0xD800 | (codePoint >> 10);
// Mask to get least significant 10 bits
var trailingSurrogate = 0xDC00 | (codePoint & TEN_BITS);
return u(leadingSurrogate) u(trailingSurrogate);
}
UCS-2,一种已弃用的格式,使用 16 位代码单元来表示(仅!)BMP 的代码点。当 Unicode 代码点的范围扩展到 16 位之外时,UTF-16 取代了 UCS-2。
UTF-8具有 8 位代码单元。它在传统 ASCII 编码和 Unicode 之间架起了一座桥梁。ASCII 只有 128 个字符,其编号与前 128 个 Unicode 代码点相同。UTF-8 是向后兼容的,因为所有 ASCII 代码都是有效的代码单元。换句话说,在范围 0–127 的单个代码单元中编码了相同范围内的单个代码点。这些代码单元的最高位为零。另一方面,如果最高位为 1,则会跟随更多的单元,以为更高的代码点提供额外的位。这导致了以下编码方案:
- 0000–007F:0xxxxxxx(7 位,存储在 1 字节中)
- 0080–07FF:110xxxxx,10xxxxxx(5 6 位=11 位,存储在 2 字节中)
- 0800–FFFF:1110xxxx,10xxxxxx,10xxxxxx(4 6 6 位=16 位,存储在 3 字节中)
- 10000–1FFFFF:11110xxx,10xxxxxx,10xxxxxx,10xxxxxx(3 6 6 6 位=21 位,存储在 4 字节中)。最高代码点是 10FFFF,因此 UTF-8 有一些额外的空间。
如果最高位不为 0,则零之前的 1 的数量表示序列中有多少个代码单元。初始单元之后的所有单元都具有位前缀 10。因此,初始代码单元和后续代码单元的范围是不相交的,这有助于从编码错误中恢复。
UTF-8 已成为最流行的 Unicode 格式。最初,它之所以受欢迎,是因为它与 ASCII 的向后兼容性。后来,它因其在操作系统、编程环境和应用程序中的广泛和一致的支持而受到青睐。
JavaScript 源代码和 Unicode
JavaScript 处理 Unicode 源代码有两种方式:内部(在解析期间)和外部(在加载文件时)。
内部源代码
在内部,JavaScript 源代码被视为一系列 UTF-16 代码单元。根据第 6 节的 EMCAScript 规范:
ECMAScript 源文本以 Unicode 字符编码的形式表示,版本为 3.0 或更高。[…] ECMAScript 源文本被假定为本规范的目的是一系列 16 位代码单元。[…] 如果实际源文本以除 16 位代码单元以外的形式编码,必须处理为首先转换为 UTF-16。
在标识符、字符串文字和正则表达式文字中,任何代码单元也可以通过 Unicode 转义序列uHHHH
来表示,其中HHHH
是四个十六进制数字。例如:
> var fu006Fu006F = 'abc';
> foo
'abc'
> var λ = 123;
> u03BB
123
这意味着您可以在源代码中使用 Unicode 字符的文字和变量名,而不会离开 ASCII 范围。
在字符串文字中,还有一种额外的转义可用:用两位十六进制数字表示的十六进制转义序列,表示范围在 0x00-0xFF 的代码单元。例如:
代码语言:javascript复制> '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">
...
强烈建议您始终指定编码。如果不指定,将使用特定于区域设置的默认编码。换句话说,在不同国家,人们将以不同方式看待文件。只有最低的 7 位在各个区域设置中相对稳定。
我的建议可以总结如下:
- 对于您自己的应用程序,您可以使用 Unicode。但必须将应用程序的 HTML 页面的编码指定为 UTF-8。
- 对于库,最安全的做法是发布 ASCII(7 位)代码。
一些缩小工具可以将具有超出 7 位的 Unicode 代码点的源代码转换为“7 位干净”的源代码。它们通过用 Unicode 转义替换非 ASCII 字符来实现。例如,以下调用UglifyJS将文件test.js翻译为:
代码语言:javascript复制uglifyjs -b beautify=false,ascii-only=true test.js
文件test.js如下所示:
代码语言:javascript复制var σ = 'Köln';
UglifyJS 的输出如下:
代码语言:javascript复制var u03c3="Kxf6ln";
考虑以下负面示例。有一段时间,库 D3.js 以 UTF-8 发布。这导致了一个错误,因为当它从编码不是 UTF-8 的页面加载时,代码包含了诸如以下语句:
代码语言:javascript复制var π = Math.PI, ε = 1e-6;
标识符π和ε没有被正确解码,也没有被识别为有效的变量名。此外,一些超出 7 位的代码点的字符串文字也没有被正确解码。作为一种解决方法,您可以通过向<script>
标签添加适当的charset
属性来加载代码:
<script charset="utf-8" src="d3.js"></script>
JavaScript 字符串和 Unicode
JavaScript 字符串是一系列 UTF-16 代码单元。根据 ECMAScript 规范,第 8.4 节:
当一个字符串包含实际文本数据时,每个元素被认为是单个 UTF-16 代码单元。
转义序列
如前所述,您可以在字符串文字中使用 Unicode 转义序列和十六进制转义序列。例如,您可以通过将o与重音符(代码点 0x0308)组合来产生字符ö:
代码语言:javascript复制> console.log('ou0308')
ö
这适用于 JavaScript 命令行,例如 Web 浏览器控制台和 Node.js REPL。您还可以将这种类型的字符串插入到 Web 页面的 DOM 中。
通过转义引用星际飞机字符
网络上有许多不错的 Unicode 符号表。看看 Tim Whitlock 的“Emoji Unicode Tables”,并对现代 Unicode 字体中有多少符号感到惊讶。表中的符号都不是图像;它们都是字体字形。假设您想通过 JavaScript 显示一个星际飞机中的 Unicode 字符(显然,这样做存在风险:并非所有字体都支持所有这些字符)。例如,考虑一头奶牛,代码点为 0x1F404:!。
您可以复制字符并直接粘贴到您的 Unicode 编码 JavaScript 源代码中:
!
JavaScript 引擎将解码源代码(通常为 UTF-8)并创建一个具有两个 UTF-16 代码单元的字符串。或者,您可以自己计算两个代码单元并使用 Unicode 转义序列。有一些网络应用程序可以执行这种计算,例如:
- UTF 转换器
- Mathias Bynens 的“JavaScript 转义”
先前定义的函数toUTF16
也执行了它:
> toUTF16(0x1F404)
'\uD83D\uDC04'
UTF-16 代理对(0xD83D,0xDC04)确实编码了奶牛:
!
计数字符
如果字符串包含代理对(两个编码单元编码一个代码点),那么length
属性不再计算图形元素。它计算编码单元:
!
这可以通过库来修复,例如 Mathias Bynens 的Punycode.js,它与 Node.js 捆绑在一起:
代码语言:javascript复制> var puny = require('punycode');
> puny.ucs2.decode(str).length
1
Unicode 规范化
如果您想在字符串中搜索或比较它们,那么您需要进行规范化,例如通过库unorm(由 Bjarke Walling)。
JavaScript 正则表达式和 Unicode
JavaScript 正则表达式中的 Unicode 支持(请参阅第十九章)非常有限。例如,没有办法匹配“大写字母”等 Unicode 类别。
行终止符影响匹配。行终止符是下表中指定的四个字符之一:
代码单元 | 名称 | 字符转义序列 |
---|---|---|
u000A | 换行符 | n |
u000D | 回车 | r |
u2028 | 行分隔符 | |
u2029 | 段落分隔符 |
以下正则表达式构造基于 Unicode:
s S
(空白,非空白)具有基于 Unicode 的定义:
> /^s$/.test('uFEFF')
true
.
(点)匹配所有代码单元(不是代码点!)除了行终止符。请参阅下一节,了解如何匹配任何代码点。
多行模式/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
匹配任何代码单元和任何代码点
要匹配任何代码单元,您可以使用[sS]
;请参见原子:通用。
要匹配任何代码点,您需要使用:¹⁸
代码语言:javascript复制([ -uD7FFuE000-uFFFF]|[uD800-uDBFF][uDC00-uDFFF])
前面的模式工作原理如下:
代码语言:javascript复制([BMP code point]|[leading surrogate][trailing surrogate])
由于所有这些范围都是不相交的,该模式将正确匹配 UTF-16 字符串中的代码点。
库
一些库可帮助处理 JavaScript 中的 Unicode:
Regenerate有助于生成像前面那样的范围,以匹配任何代码单元。它旨在用作构建工具的一部分,但也可以动态工作,用于尝试各种功能。
XRegExp是一个正则表达式库,它有一个官方附加组件,可以通过以下三种构造之一匹配 Unicode 类别、脚本、块和属性:
代码语言:javascript复制p{...} p{^...} P{...}
例如,p{Letter}
匹配各种字母表中的字母,而p{^Letter}
和P{Letter}
都匹配所有其他代码点。第三十章包含了对 XRegExp 的简要概述。
- ECMAScript 国际化 API(请参见ECMAScript 国际化 API)提供了对 Unicode 的排序和搜索等功能。
推荐阅读和章节来源
有关 Unicode 的更多信息,请参见以下内容:
- 维基百科上有几篇关于Unicode及其术语的好文章。
- Unicode.org,Unicode 联盟的官方网站,以及其FAQ也是很好的资源。
- Joel Spolsky 的介绍性文章“每个软件开发人员绝对必须了解的有关 Unicode 和字符集的绝对最低限度(没有借口!)”很有帮助。
有关 JavaScript 中的 Unicode 支持的信息,请参见:
- Mathias Bynens 的文章“JavaScript 的内部字符编码:UCS-2 还是 UTF-16?”
- 《JavaScript,正则表达式和 Unicode》由 Steven Levithan
致谢
以下人员为本章做出了贡献:Mathias Bynens(@mathias),Anne van Kesteren(@annevk)和 Calvin Metcalf(@CWMma)。
¹⁸ 严格来说,任何Unicode 标量值。
第二十五章:ECMAScript 5 中的新功能
原文:25. New in ECMAScript 5 译者:飞龙 协议:CC BY-NC-SA 4.0
本章列出了仅在 ECMAScript 5 中可用的功能。如果您必须使用旧版 JavaScript 引擎,您应该避免使用这些功能,或者通过库启用其中一些功能(稍后将进行描述)。请注意,通常情况下,本书假定您正在使用完全支持 ECMAScript 5 的现代引擎。
ECMAScript 5 规范包含了对其范围的以下描述:
ECMAScript 的第五版(作为 ECMA-262 第 5 版发布)
- 对已成为浏览器实现中常见的语言规范的实际解释进行了编码
- 增加了对自第三版出版以来出现的新功能的支持。这些功能包括
- 访问器属性,
- 反射创建和检查对象,
- 程序控制属性属性,
- 附加数组操作函数,
- 对 JSON 对象编码格式的支持,以及 x
- 提供增强的错误检查和程序安全性的严格模式。
新功能
ECMAScript 5 中包含的新功能如下:
严格模式(参见严格模式)
将以下行放在文件或函数的开头可以打开所谓的严格模式,使 JavaScript 成为一个更干净的语言,禁止一些功能,执行更多检查,并抛出更多异常:
代码语言:javascript复制'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 的标准库带来了几个新增功能。本节按类别列出了它们。
元编程
获取和设置原型(参见获取和设置原型):
-
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 的浏览器中。