5.7 内置对象
ECMA-262对内置对象的定义是:“由 ECMAScript 实现提供的、不依赖宿主环境的对象,这些对象在 ECMAScript 程序执行之前就已经存在了。”意思就是说,开发人员不必显式地实例化内置对象;因为它们已经实例化了。ECMA-262 只定义了两个内置对象:Global 和 Math 。
5.7.1 Global 对象
Global (全局) 对象可以说是 ECMAScript 中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。ECMAScript 中的 Global 对象在某种意义上是作为一个终极的 "兜底儿对象" 来定义的。换句话说,不属于任何其他对象的属性和方法,最终都是它的属性和方法。事实上,没有全局变量或全局函数;所有在全局作用域中定义的属性和函数,都是 Global 对象的属性。本书前面介绍过的那些函数,诸如 isNaN()、isFinite()、parseInt() 以及 parseFloat() ,实际上全都是 Global 对象的方法。除此之外,Global 对象还包含其他一些方法。
1. URI 编码方法
Global 对象的 encodeURI() 和 encodeURIComponent() 方法可以对 URI (Uniform Resource Identifiers,通用资源标识符) 进行编码,以便发送给浏览器。有效的URI中不能包含某些字符,例如空格。而这两个 URI 编码方法就可以对 URI 进行编码,它们用特殊的 UTF-8 编码替换所有无效的字符,从而让浏览器能够接受和理解。
其中,encodeURI() 主要用于整个 URI(例如,http://www.wrox.com/illegal value.htm) ,而encodeURIComponent() 主要用于对 URI 中的某一段(例如前面 URI 中的 illegal value.htm) 进行编码。它们的主要区别在于,encodeURI() 不会对本身属于 URI 的特殊字符进行编码,例如冒号、正斜杠、问号和井字号;而 encodeURIComponent() 则会对它发现的任何非标准字符进行编码。来看下面的例子:
var uri = "http://www.wrox.com/illegal value.htm#start";
// "http://www.wrox.com/illegal%20value.htm#start"
alert(encodeURI(uri));
// "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start"
alert(encodeURIComponent(uri));
使用 encodeURI() 编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了 %20 。而 encodeURIComponent() 方法则会使用对应的编码替换所有非字母数字字符。这也正是可以对整个 URI 使用 encodeURI() ,而只能对附加在现在 URI 后面的字符串使用 encodeURIComponent() 的原因所在。
一般来说,我们使用 encodeURIComponent() 方法的时候要比使用 encodeURI() 更多,因为在实践中更常见的是对查询字符串参数而不是对基础 URI 进行编码。
与 encodeURI() 和 encodeURIComponenet() 方法对应的两个方法分别是 decodeURI() 和 decodeURIComponent() 。其中,decodeURI() 只能对使用 encodeURI() 替换的字符进行解码。例如,它可将 20% 替换成一个空格,但不会对 %23作任何处理,因为 %23表示井字号 (#) ,而井字号不是使用 encodeURI() 替换的。同样地,decodeURIComponent() 能够解码使用 encodeURIComponent() 编码的所有字符,即它可以解码任何特殊字符的编码。来看下面的例子:
var uri = "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start";
// http%3A%2F%2Fwww.wrox.com%2Fillegal value.htm%23start
alert(decodeURI(uri));
// http://www.wrox.com/illegal value.htm#start
alert(decodeURIComponent(uri));
这里,变量 uri 包含着一个由 encodeURIComponent() 编码的字符串。在第一次调用 decodeURI() 输出的结果中,只有 %20 被替换成了空格。而在第二次调用 decodeURIComponent() 输出的结果中,所有特殊字符的编码都被替换成了原来的字符,得到了一个未经转义的字符串(但这个字符串并不是一个有效的 URI) 。
URI 方法 encodeURI() 、encodeURIComponent()、decodeURI()和decodeURIComponent()用于替代已经被 ECMA-262 第3版废弃的 escape() 和 unescape() 方法。URI 方法能够编码所有 Unicode 字符,而原来的方法只能正确地编码 ASCII 字符。因此在开发实践中,特别是在产品级的代码中,一定要使用 URI 方法,不要使用 escape() 和 unescape() 方法。
2.eval() 方法
现在,我们介绍最后一个 -- 大概也是整个 ECMAScript 语言中最强大的一个方法: eval() 。eval() 方法就像是一个完整的 ECMAScript 解析器,它只接受一个参数,即要执行的ECMAScript (或 JavaScript) 字符串。看下面的例子:
eval("alert('hi')");
这行代码的作用等价于下面这行代码:
alert("hi");
当解析器发现代码中调用 eval() 方法时,它会将传入的参数当作实际的 ECMAScript 语句来解析,然后把执行结果插入到原位置。通过 eval() 执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。这意味着通过 eval() 执行的代码可以引用在包含环境中定义的变量,举个例子:
var msg = "hello world";
eval("alert(msg)"); // "hello world"
可见,变量 msg 是在 eval() 调用的环境之外定义的,但其中调用的 alert() 仍然能够显示 "hello world" 。这是因为上面第二行代码最终被替换成了一行真正的代码。同样地,我们也可以在 eval() 调用中定义一个函数,然后再在该调用的外部代码中引用这个函数:
eval("function sayHi() { alert('hi'); }");
sayHi();
显然,函数 sayHi() 是在 eval() 内部定义的。但由于对 eval() 的调用最终会被替换成定义函数的实际代码,因此可以在下一行调用 sayHi() 。
能够解释代码字符串的能力非常强大,但也非常危险。因此在使用 eval() 时必须极为谨慎,特别是用它执行用户输入数据的情况下。否则,可能会有恶意用户输入威胁你的站点或应用程序安全的代码 (即所谓的代码注入) 。
3.Global 对象的属性
Global 对象还包含一些属性,其中一部分属性已经在本书前面介绍过了。例如,特殊的值 undefined、NaN以及 Infinity 都是 Global 对象的属性。此外,所有原生引用类型的构造函数,像 Object 和 Function ,也都是 Global 对象的属性。下表列出了 Global 对象的所有属性。
4.window 对象
ECMAScript 虽然没有指出如何直接访问 Global 对象,但 Web 浏览器都是将这个全局对象作为 window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window 对象的属性。来看下面的例子:
var color = "red";
function sayColor() {
alert(window.color);
}
window.sayColor(); // "red"
这里定义了一个名为 color 的全局变量和一个名为 sayColor() 的全局函数。在 sayColor() 内部,我们通过 window.color 来访问 color 变量,以说明全局变量是 window 对象的属性。然后,又使用 window.sayColor() 来直接通过 window 对象调用这个函数,结果显示在了警告框中。
JavaScript 中的 window 对象除了扮演 ECMAScript 规定的 Global 对象的角色外,还承担了很多别的任务。第 8 章在讨论浏览器对象模型时将详细介绍 window 对象。
5.7.2 Math 对象
ECMAScript 还为保存数学公式和信息提供了一个公共位置,即 Math 对象。与我们在 JavaScript 直接编写的计算功能相比,Math 对象提供的计算功能执行起来要快得多。Math 对象中还提供了辅助完成这些计算的属性和方法。
1.Math 对象的属性
Math 对象包含的属性大都是数学计算中可能会用到的一些特殊值。下表列出了这些属性。
虽然讨论这些值的含义和用途超出了本书范围,但你确实可以随时使用它们。
2.min() 和 max() 方法
Math 对象还包含许多方法,用于辅助完成简单和复杂的数学计算。
其中,min() 和 max() 方法用于确定一组数值中的最小值和最大值。这两个方法都可以接收任意多个数值参数,如下面的例子所示:
var max = Max.max(3, 54, 32, 16);
alert(max); // 54
var min = Math.min(3, 54, 32, 16);
alert(min); // 3
对于 3 、 54 、 32 和 16,Math.max() 返回 54,而 Math.min() 返回 3 。这两个方法经常用于避免多余的循环和在 if 语句中确定一组数的最大值。
3.舍入方法
下面来介绍将小数值舍入为整数的几个方法:Math.ceil()、Math.floor() 和 Math.round()。
这三个方法分别遵循下列舍入规则:
- Math.ceil() 执行向上舍入,即它总是将数值向上舍入为最接近的整数;
- Math.floor() 执行向下舍入,即它总是将数值向下舍入为最接近的整数;
- Math.round() 执行标准舍入,即它总是将数值四舍五入为最接近的整数 (这也是我们在数学课上学到的舍入规则)。
