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

ECMA-262 對內置對象的定義是「由 JavaScript 實現提供的、不依賴於宿主環境的對象,這些對象在 JavaScript 程序執行以前就已經存在了」。意思就是說,開發人員沒必要顯式地實例化內置對象,由於它們已經實例化了。前面咱們已經介紹了大多數內置對象,例如 ObjectArrayString。ECMA-262 還定義了兩個單體內置對象:GlobalMathjavascript

Global 對象

Global 對象能夠說是 JavaScript 中最特別的一個對象了,由於無論你從什麼角度上看,這個對象都是不存在的。Global 對象在某種意義上是做爲一個終極的「兜底兒對象」來定義的。換句話說,不屬於任何其餘對象的屬性和方法,最終都是它的屬性和方法。全部在全局做用域中定義的屬性和函數,都是 Global 對象的屬性。本書前面介紹過的那些函數,諸如 isNaN()isFinite()parseInt() 以及 parseFloat(),實際上全都是 Global 對象的方法。除此以外,Global 對象還包含其餘一些方法。java

URI 編碼方法

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

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

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

這行代碼的做用等價於下面這行代碼:app

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 對象還包含一些屬性,其中一部分屬性已經在本書前面介紹過了。例如,特殊的值 undefinedNaN 以及 Infinity 都是 Global 對象的屬性。此外,全部原生引用類型的構造函數,像 ObjectFunction,也都是 Global 對象的屬性。下表列出了 Global 對象的全部屬性。

屬性 說明 屬性 說明
undefined 特殊值undefined Date 構造函數Date
NaN 特殊值NaN RegExp 構造函數RegExp
Infinity 特殊值Infinity Error 構造函數Error
Object 構造函數Object EvalError 構造函數EvalError
Array 構造函數Array RangeError 構造函數RangeError
Function 構造函數Function ReferenceError 構造函數ReferenceError
Boolean 構造函數Boolean SyntaxError 構造函數SyntaxError
String 構造函數String TypeError 構造函數TypeError
Number 構造函數Number URIError 構造函數URIError

ECMAScript 5 明確禁止給 undefinedNaNInfinity 賦值,這樣作即便在非嚴格模式下也會致使錯誤。

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 對象包含的屬性大都是數學計算中可能會用到的一些特殊值。下表列出了這些屬性。

屬性 說明
Math.E 天然對數的底數,即常量e的值
Math.LN10 10的天然對數
Math.LN2 2的天然對數
Math.LOG2E 以2爲底e的對數
Math.LOG10E 以10爲底e的對數
Math.PI π的值
Math.SQRT1_2 1/2的平方根(即2的平方根的倒數)
Math.SQRT2 2的平方根

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 對象的方法。

方法 說明
Math.abs(num) 返回num的絕對值
Math.asin(x) 返回x的反正弦值
Math.exp(num) 返回Math.E的num次冪
Math.atan(x) 返回x的反正切值
Math.log(num) 返回num的天然對數
Math.atan2(y,x) 返回y/x的反正切值
Math.pow(num,power) 返回num的power次冪
Math.cos(x) 返回x的餘弦值
Math.sqrt(num) 返回num的平方根
Math.sin(x) 返回x的正弦值
Math.acos(x) 返回x的反餘弦值
Math.tan(x) 返回x的正切值

雖然 ECMA-262 規定了這些方法,但不一樣實現可能會對這些方法採用不一樣的算法。畢竟,計算某個值的正弦、餘弦和正切的方式多種多樣。也正由於如此,這些方法在不一樣的實現中可能會有不一樣的精度。

關卡

// 如何高效產生m個n範圍內的不重複隨機數(m<=n)
var getRandomNumber = function(n, m){
    // 待實現方法體
}
console.log(getRandomNumber(20, 3));  // 8,4,19

更多

關注微信公衆號「劼哥舍」回覆「答案」,獲取關卡詳解。
關注 https://github.com/stone0090/javascript-lessons,獲取最新動態。

相關文章
相關標籤/搜索