首頁  >  文章  >  web前端  >  《JavaScript闖關記》之單體內建對象

《JavaScript闖關記》之單體內建對象

高洛峰
高洛峰原創
2016-11-07 16:35:041080瀏覽

ECMA-262 內建物件的定義是「由 JavaScript 實作提供的、不依賴宿主環境的對象,這些物件在 JavaScript 程式執行之前就已經存在了」。意思是說,開發人員不必明確地實例化內建對象,因為它們已經實例化了。前面我們已經介紹了大多數內建對象,例如Object、Array 和 String。 ECMA-262 也定義了兩個單體內建物件:Global 和 Math。

Global 物件

Global 物件可以說是 JavaScript 中最特別的一個物件了,因為不管你從什麼角度來看,這個物件都是不存在的。 Global 物件在某種意義上是作為一個終極的「兜底兒物件」來定義的。換句話說,不屬於任何其他物件的屬性和方法,最終都是它的屬性和方法。所有在全域作用域中定義的屬性和函數,都是 Global 物件的屬性。本書前面介紹過的那些函數,像是isNaN()、isFinite()、parseInt() 以及 parseFloat(),其實全都是 Global 物件的方法。除此之外,Global 物件還包含其他一些方法。

URI 編碼方法

Global 物件的 encodeURI() 和 encodeURIComponent() 方法可以對 URI(Uniform Resource Identifiers,通用資源識別碼)進行編碼,以便傳送給瀏覽器。有效的 URI 中不能包含某些字符,例如空格。而這兩個 URI 編碼方法就可以對 URI 進行編碼,它們用特殊的 UTF-8 編碼替換所有無效的字符,從而讓瀏覽器能夠接受和理解。

其中,encodeURI() 主要用於整個 URI,而 encodeURIComponent() 主要用於對 URI 中的某一段進行編碼。它們的主要區別在於,encodeURI() 不會對本身屬於 URI 的特殊字元進行編碼,例如冒號、正斜線、問號和井字號;而 encodeURIComponent() 則會對它發現的任何非標準字元進行編碼。來看下面的例子。

var uri = "http://shijiajie.com/illegal value.htm#start"; 
 
console.log(encodeURI(uri)); 
// "http://shijiajie.com/illegal%20value.htm#start" 
 
console.log(encodeURIComponent(uri)); 
// "http%3A%2F%2Fshijiajie.com%2Fillegal%20value.htm%23start"

使用 encodeURI() 編碼後的結果是除了空格之外的其他字元都原封不動,只有空格被替換成了 %20。而 encodeURIComponent() 方法則會使用對應的編碼來取代所有非字母數字字元。這也正是整個 URI 可以使用 encodeURI(),而只能對附加在現有 URI 後面的字串使用 encodeURIComponent() 的原因所在。

一般來說,我們使用 encodeURIComponent() 方法的時候要比使用 encodeURI() 更多,因為在實踐中更常見的是對查詢字串參數而不是對基礎 URI 進行編碼。

與 encodeURI() 和 encodeURIComponent() 方法對應的兩個方法分別是 decodeURI() 和 decodeURIComponent()。其中,decodeURI() 只能對使用 encodeURI() 替換的字元進行解碼。例如,它可將 %20 替換成一個空格,但不會對 %23 作任何處理,因為 %23 表示井字號 #,而井字號不是使用 encodeURI() 替換的。同樣地,decodeURIComponent() 能夠解碼使用encodeURIComponent() 編碼的所有字符,即它可以解碼任何特殊字符的編碼。來看下面的範例:

var uri = "http%3A%2F%2Fshijiajie.com%2Fillegal%20value.htm%23start"; 
 
console.log(decodeURI(uri)); 
// http%3A%2F%2Fshijiajie.com%2Fillegal value.htm%23start 
 
console.log(decodeURIComponent(uri)); 
// http://shijiajie.com/illegal value.htm#start

這裡,變數 uri 包含著一個由 encodeURIComponent() 編碼的字串。在第一次呼叫 decodeURI() 輸出的結果中,只有 %20 被替換成了空格。而在第二次調用decodeURIComponent() 輸出的結果中,所有特殊字符的編碼都被替換成了原來的字符,得到了一個未經轉義的字符串(但這個字符串並不是一個有效的URI) 。

eval() 方法

eval() 方法就像是一個完整的 JavaScript 解析器,它只接受一個參數,即要執行的 JavaScript 字串。看下面的例子:

eval("console.log('hi')");

這行程式碼的作用等價於下面這行程式碼:

console.log("hi");

當解析器發現程式碼中呼叫eval() 方法時,它會將傳入的參數當作實際的JavaScript 語句來解析,然後把執行結果插入到原位置。透過 eval() 執行的程式碼被視為包含該次呼叫的執行環境的一部分,因此被執行的程式碼具有與該執行環境相同的作用域鏈。這意味著透過eval() 執行的程式碼可以引用在包含環境中定義的變量,舉個例子:

var msg = "hello world";  eval("console.log(msg)"); // "hello world"  

可見,變數msg 是在eval() 呼叫的環境之外定義的,但其中所呼叫的console.log() 仍然能夠顯示"hello world"。這是因為上面第二行程式碼最終被替換成了一行真正的程式碼。同樣地,我們也可以在eval() 呼叫中定義一個函數,然後在該呼叫的外部程式碼中引用這個函數:

eval("function sayHi() { console.log('hi'); }" );  sayHi(); // "hi"  

顯然,函數sayHi() 是在eval() 內部定義的。但由於對 eval() 的呼叫最終會被替換成定義函數的實際程式碼,因此可以在下一行呼叫 sayHi() 。對於變數也一樣:

eval("var msg = 'hello world';"); 
console.log(msg); // "hello world"

在 eval() 中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中;它们只在 eval() 执行的时候创建。

严格模式下,在外部访问不到 eval() 中创建的任何变量或函数,因此前面两个例子都会导致错误。同样,在严格模式下,为 eval赋值也会导致错误:

"use strict"; eval =
"hi"; // causes error

能够解释代码字符串的能力非常强大,但也非常危险。因此在使用 eval() 时必须极为谨慎,特别是在用它执行用户输入数据的情况下。否则,可能会有恶意用户输入威胁你的站点或应用程序安全的代码(即所谓的代码注入)。

Global 对象的属性

Global 对象还包含一些属性,其中一部分属性已经在本书前面介绍过了。例如,特殊的值 undefined、NaN 以及 Infinity 都是Global 对象的属性。此外,所有原生引用类型的构造函数,像 Object 和 Function,也都是 Global 对象的属性。下表列出了Global 对象的所有属性。

《JavaScript闖關記》之單體內建對象

ECMAScript 5 明确禁止给 undefined、NaN 和 Infinity 赋值,这样做即使在非严格模式下也会导致错误。

window 对象

JavaScript 虽然没有指出如何直接访问 Global 对象,但 Web 浏览器都是将这个全局对象作为 window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window 对象的属性。来看下面的例子。

var color = "red"; 
 
function sayColor(){ 
    console.log(window.color); 
} 
 
window.sayColor();  // "red"

JavaScript 中的 window 对象除了扮演规定的 Global 对象的角色外,还承担了很多别的任务。

Math 对象

JavaScript 还为保存数学公式和信息提供了一个公共位置,即 Math 对象。与我们在 JavaScript 直接编写的计算功能相比,Math对象提供的计算功能执行起来要快得多。Math 对象中还提供了辅助完成这些计算的属性和方法。

Math 对象的属性

Math 对象包含的属性大都是数学计算中可能会用到的一些特殊值。下表列出了这些属性。

属性说明

《JavaScript闖關記》之單體內建對象

min() 和 max() 方法

Math 对象还包含许多方法,用于辅助完成简单和复杂的数学计算。其中,min() 和 max() 方法用于确定一组数值中的最小值和最大值。这两个方法都可以接收任意多个数值参数,如下面的例子所示。

var max = Math.max(3, 54, 32, 16); 
 
console.log(max); // 54 
 
var min = Math.min(3, 54, 32, 16); 
 
console.log(min); // 3

要找到数组中的最大或最小值,可以像下面这样使用 apply() 方法。

var values = [1, 2, 3, 4, 5, 6, 7, 8]; 
 
var max = Math.max.apply(Math, values); 
 
console.log(max); // 8

这个技巧的关键是把 Math 对象作为 apply() 的第一个参数,从而正确地设置 this 值。然后,可以将任何数组作为第二个参数。

舍入方法

下面来介绍将小数值舍入为整数的几个方法:Math.ceil()、Math.floor() 和 Math.round()。这三个方法分别遵循下列舍入规则:

Math.ceil() 执行向上舍入,即它总是将数值向上舍入为最接近的整数;

Math.floor() 执行向下舍入,即它总是将数值向下舍入为最接近的整数;

Math.round() 执行标准舍入,即它总是将数值四舍五入为最接近的整数。

下面是使用这些方法的示例:

console.log(Math.ceil(25.9));     // 26 
console.log(Math.ceil(25.5));     // 26 
console.log(Math.ceil(25.1));     // 26 
 
console.log(Math.round(25.9));    // 26 
console.log(Math.round(25.5));    // 26 
console.log(Math.round(25.1));    // 25 
 
console.log(Math.floor(25.9));    // 25 
console.log(Math.floor(25.5));    // 25 
console.log(Math.floor(25.1));    // 25

random() 方法

Math.random() 方法返回介于0和1之间一个随机数,不包括0和1。对于某些站点来说,这个方法非常实用,因为可以利用它来随机显示一些名人名言和新闻事件。套用下面的公式,就可以利用 Math.random() 从某个整数范围内随机选择一个值。

值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值)

公式中用到了 Math.floor() 方法,这是因为 Math.random() 总返回一个小数值。而用这个小数值乘以一个整数,然后再加上一个整数,最终结果仍然还是一个小数。举例来说,如果你想选择一个1到10之间的数值,可以像下面这样编写代码:

var num = Math.floor(Math.random() * 10 + 1);

总共有10个可能的值(1到10),而第一个可能的值是1。而如果想要选择一个介于2到10之间的值,就应该将上面的代码改成这样:

var num = Math.floor(Math.random() * 9 + 2);

从2数到10要数9个数,因此可能值的总数就是9,而第一个可能的值就是2。多数情况下,其实都可以通过一个函数来计算可能值的总数和第一个可能的值,例如:

function selectFrom(lowerValue, upperValue) { 
    var choices = upperValue - lowerValue + 1; 
    return Math.floor(Math.random() * choices + lowerValue); 
} 
 
var num = selectFrom(2, 10); 
console.log(num);   // 介于2和10之间(包括2和10)的一个数值

函数 selectFrom() 接受两个参数:应该返回的最小值和最大值。而用最大值减最小值再加1得到了可能值的总数,然后它又把这些数值套用到了前面的公式中。这样,通过调用 selectFrom(2,10) 就可以得到一个介于2和10之间(包括2和10)的数值了。利用这个函数,可以方便地从数组中随机取出一项,例如:

var colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"]; 
 
var color = colors[selectFrom(0, colors.length-1)]; 
 
console.log(color); // 可能是数组中包含的任何一个字符串

其他方法

Math 对象中还包含其他一些与完成各种简单或复杂计算有关的方法,但详细讨论其中每一个方法的细节及适用情形超出了本书的范围。下面我们就给出一个表格,其中列出了这些没有介绍到的 Math 对象的方法。

《JavaScript闖關記》之單體內建對象

虽然 ECMA-262 规定了这些方法,但不同实现可能会对这些方法采用不同的算法。毕竟,计算某个值的正弦、余弦和正切的方式多种多样。也正因为如此,这些方法在不同的实现中可能会有不同的精度。

关卡

// 如何高效产生m个n范围内的不重复随机数(m<=n) 
var getRandomNumber = function(n, m){ 
  // 待实现方法体 
} 
console.log(getRandomNumber(20, 3)); // 8,4,19


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn