第 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 也不会对其进行解码,即使它们已被正确编码

> 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?, ...)
将参数记录到控制台。在浏览器中,记录的内容可能会标有“警告”图标,并且/或者包含堆栈跟踪或指向代码的链接。

下表显示了在各种平台上的支持情况

ChromeFirebugFirefoxIENode.jsSafari

clear

debug

error

exception

info

log

trace

warn

exception 已被设置为斜体,因为它仅在一个平台上受支持。

检查和计数

控制台 API 包括以下 检查和计数方法:

console.assert(expr, obj?)
如果 exprfalse,则将 obj 记录到控制台并抛出异常。如果为 true,则不执行任何操作。
console.count(label?)
统计带有此标签的语句执行了多少次。

下表显示了在各种平台上的支持情况

ChromeFirebugFirefoxIENode.jsSafari

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']);

生成的表如下

(索引)firstNamelastNameage

0

“Jane”

“Bond”

undefined

1

“Lars”

“Croft”

72

下表显示了在各种平台上的支持情况

ChromeFirebugFirefoxIENode.jsSafari

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 的时间戳。可以记录到控制台或时间线。

下表显示了在各种平台上的支持情况

ChromeFirebugFirefoxIENode.jsSafari

markTimeline

profile

(开发者工具)

profileEnd

(开发者工具)

time

timeEnd

timeStamp

markTimeline 已被设置为斜体,因为它仅在一个平台上受支持。(开发者工具)表示必须打开开发者工具才能使该方法正常工作。[19]

命名空间和特殊值

以下全局变量用作函数的命名空间。有关详细信息,请参阅括号中指示的材料:

JSON
JSON API 功能(第 22 章
Math
Math API 功能(第 21 章
Object
元编程功能(备忘单:使用对象

以下全局变量包含特殊值。有关它们的更多信息,请查看括号中指示的材料:

undefined

表示某事物不存在的值(undefined 和 null

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

表示某事物“不是数字”的值(NaN

> 1 / 'abc'
NaN
Infinity

表示数字无穷大 ∞ 的值(Infinity

> 1 / 0
Infinity



[18] Mariusz Nowak (@medikoo) 告诉我,由 Function 计算的代码在任何地方默认都是 sloppy 模式。

[19] 感谢 Matthias Reuter (@gweax) 和 Philipp Kyeck (@pkyeck) 对本节的贡献。

下一页:24. Unicode 和 JavaScript