JavaScript高級程序設計(讀書筆記)(五)

本筆記彙總了作者認爲“JavaScript高級程序設計”這本書的前七章知識重點,僅供參考。

第五章 引用類型

小結:
對象在JavaScript中被稱爲引用類型的值,而且有一些內置的引用類型可以用來創建特定的對象,現簡要總結如下:

  • 引用類型與傳統面向對象程序設計中的類相似,但實現不同;
  • Object是一個基礎類型,其他所有類型都從Object繼承了基本的行爲;
  • Array類型是一組值得有序列表,同時還提供了操作和轉換這些值的功能;
  • Date類型提供了有關日期和時間的信息,包括當前日期和時間以及相關的計算功能;
  • RegExp類型是ECMAScript支持正則表達式的一個接口,提供了最基本的和一些高級的正則表達式工功能。

函數實際上是Function類型的實例,因此函數也是對象;而這一點正是JavaScript最有特色的地方。由於函數是對象,所以函數也擁有方法,可以用來增強其行爲。
因爲有了基本包裝類型,所以JavaScript中的基本類型值可以被當作對象來訪問。三種基本包裝類型分別是:Boolean、Number和String。以下是他們共同的特徵:

  • 每個包裝類型都映射到同名的基本類型;
  • 在讀取模式下訪問基本類型值時,就會創建對應的基本包裝類型的一個對象,從而方便了數據的操作;
  • 操作基本類型值的語句一經執行完畢,就會立即銷燬新創建的對象。

在所有代碼執行之前,作用域中就已經存在兩個內置對象:Global和Math。在大多數ECMAScript實現中都不能直接訪問Global對象;不過,Web瀏覽器實現了承擔該角色的window對象。全局變量和函數都是Global對象的屬性。Math對象提供了很多屬性和方法,用於輔助完成複雜的數學計算任務。

ECMAScript從技術上講是一門面嚮對象語言,但它不具備傳統的面嚮對象語言所支持的類和接口等基本結構。引用類型有時也被稱爲對象定義,因爲它們描述的是一類對象所具有的屬性和方法。對象是某個特定引用類型的實例


Object類型

大多數引用類型值都是Object類型的實例,Object是ECMAScript中使用最多的一個類型。創建Object實例的方式有兩種。第一種是使用new操作符後跟Object構造函數,如

var person = new Object();
person.name = "Nicholas";
person.age = 29;

另一種是使用對象字面量表示法,如

var person = {
    name : "Nicholas",
    age : 29
};

一般來說,訪問對象屬性時使用的都是點表示法,JavaScript中也可以使用方括號表示法來訪問對象的屬性。如

alert(person["name"]);              //"Nicholas"
alert(person.name);                 //"Nicholas"

Array類型

ECMAScript中的數組與其他多數語言中的數組有着很大的區別。雖然ECMAScript數組與其他語言中的數組都是數據的有序列表,但與其他語言不同的是,ECMAScript數組的每一項可以保存任何類型的數據。

表示方法
創建數組的基本方式有兩種。第一種是使用Array構造函數,如果預先知道數組要保存的項目數量,也可以給構造函數傳遞該數量,而該數量會自動變成length屬性的值;給構造函數傳遞值也可以創建數組。如

var colors = new Array();//空數組
var colors = new Array(20);//創建一個包含3項的數組
var colors = new Array("red", "blue", "green");//創建一個包含三個字符串值的數組
var names = Array("Greg");//創建一個包含一個字符串值的數組

第二種是使用數組字面量表示法。如

var colors = ["red", "blue", "green"];//創建一個包含3個字符串的數組
var names = [];//創建一個空數組
var values = [1,2,];//禁忌,會創建一個包含2或3項的數組
var options = [, , , , ,];//禁忌,會創建一個包含5或6項的數組

length屬性
數組length的屬性不是隻讀的,通過設置這個屬性,可以從數組的末尾移除項或向數組中添加新項。

檢測數組
自從ECMAScript3作出規定後,就出現了確定某個對象是不是數組的經典問題。對於一個網頁,或者一個全局作用域而言,使用instanceof操作符就能得到滿意結果。

if (value instanceof Array) {
    //對數組執行某些操作
}

如果網頁中包含多個框架,那實際上就存在兩個以上不同的全局執行環境,從而存在兩個以上不同版本的Array構造函數。如果從一個框架向另一個框架傳入一個數組,那麼傳入的數組與第二個框架中原生創建的數組分別具有各自不同的構造函數。爲了解決這個問題,ECMAScript5新增了Array.isArray()方法。

if (Array.isArray(value)) {
    //對數組執行某些操作
}

轉換方法

toLocaleString() :
toString() :
valueOf() :

棧方法和隊列方法

ECMAScript提供了一種讓數組的行爲類似於其他數據結構的方法。

實現棧的方式(LIFO)

  • push(): 棧中項插入
  • pop():棧中項移除

實現隊列的方法(FIFO)

  • push():向數組末端添加項
  • shift():從數組前段取得項

重排序方法

數組中已經存在兩個可以直接用來重排序的方法:reverse()sort()方法。

reverse()方法會反轉數組項的順序。

默認情況下,sort()方法按升序排列數組項。爲了實現排序,sort()方法會調用每個數組項的toString()轉型方法,然後比較得到的字符串,以確定如何排序。
另外,sort()方法可以接收一個比較函數作爲參數。比較函數接受兩個參數,如果第一個參數應該位於第二個之前則返回一個負數,如果兩個參數相等則返回0,如果第一個參數應該位於第二個之後則返回一個正數。實際操作可以根據需要自行定義。如最簡單的一個例子

function compare(value1, value2)    {
    if (value1 < value2) {
        return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
}

只需將其作爲參數傳遞給sort()方法即可。

var values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values);  //0, 1, 5, 10, 15

另外,對於數值類型或者其valueOf()方法會返回數值類型的對象類型,可以使用一個更簡單的比較函數。

function compare(value1, value2) {
    return value2 - value1;
}

操作方法

ECMAScript爲操作已經包含在數組中的項提供了很多內置方法。

concat()
該方法基於當前數組的所有項創建一個新數組。在沒有參數時返回副本,接收參數會添加到數組末尾,如果接收的是數組,則數組每一項添加到末尾。如

var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"];

alert(colors);  //red, green, blue
alert(colors2);     //red, green, blue, yellow, black, brown

slice()
該方法能夠基於當前數組的一或多個項創建一個新數組。slice()方法接收一或兩個參數,即要返回項的起始和結束位置。

var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1, 4);

alert(colors2);     //green, blue, yellow, purple
alert(colors3);     //green, blue, yellow

splice()
該方法非常強大,用法很多。它的主要用途是向數組的中部插入項,使用方式有3中:

  • 刪除:可以刪除任意數量的項, 只需指定兩個參數:要刪除的第一項的位置和要刪除的項數。例如,splice(0, 2)會刪除數組中的前兩項。
  • 插入:可以向指定位置插入任意數量的項,只需提供三個參數:起始位置、0(要刪除的項數)和要插入的項。如果要插入多個項,可以再傳入第四、第五,甚至任意多個項。例如splice(2, 0, "red", "green")會從當前數組的位置2開始插入字符串”red”和”green”。
  • 替換:可以向指定位置插入任意數量的項,且同時刪除任意數量的項,只需提供三個參數:起始位置、要刪除的項數和要插入的項。如果要插入多個項,可以再傳入第四、第五,甚至任意多個項。例如splice(2, 1, "red", "green")會刪除當前數組位置2的項,然後再從當前數組的位置2開始插入字符串”red”和”green”。

splice()方法始終都會返回一個數組,該數組中包含從原始數組中刪除的項(如果沒有刪除任何項,則返回一個空數組)。下面的代碼展示了上述3中用法:

var colors = ["red", "green", "blue"];
var removed = colors.splice(0, 1);      //刪除第一項
alert(colors);      //green, blue
alert(removed); //red, 返回的數組中只包含一項

removed = colors.splice(1, 0, "yellow", "orange");      //從位置1開始插入兩項
alert(colors);      //green, yellow, orange, blue
alert(removed); //返回的是一個空數組

removed = colors.splice(1, 1, "red", "purple");     //插入兩項,刪除一項
alert(colors);      //green, red, purple, orange, blue
alert(removed); //yellow, 返回的數組中只包含一項

位置方法

ECMAScript5爲數組實例添加了兩個位置方法:indexOf()lastIndexOf()。這兩個方法都接受兩個參數:要查找的項和(可選的)表示查找起點位置的索引。不同的是,indexOf()方法從數組的開頭向後查找,laseIndexOf()方法則從數組的末尾開始向前查找。
這兩個方法都返回要查找的項在數組中的位置,沒找到返回-1.在比較第一個參數與數組中的每一項時,會使用全等操作符;也就是說,要查找的項必須嚴格相等。

var person = {  name: "Nicholas" };
var people = [{ name: "Nicholas" }];

var morePeople = [person];

alert(people.indexOf(person));          //-1
alert(morePeople.indexOf(person));  //0

迭代方法

ECMAScript5爲數組定義了5個迭代方法。每個方法都接受兩個參數:要在每一項上運行的函數和(可選的)運行該函數的作用域——影響this的值。傳入這些方法中的函數會接收三個參數:數組項的值、該想在數組中的位置和數組對象本身。根據使用的方法不同,這個函數執行後的返回值可能會也可能不會影響方法的返回值。以下是這5個迭代方法的作用。

  • every():對數組中的每一項運行給定函數,如果該函數對每一項都返回true,則返回true。
  • some():對數組中的每一項運行給定函數,如果該函數對任一項返回true,則返回true。
  • filter():對數組中的每一項運行給定函數,返回該函數會返回true的項組成的數組。
  • forEach():對數組中的每一項運行給定函數。這個方法沒有返回值。
  • map():對數組中的每一項運行給定函數,返回每次函數調用的結果組成的數組。

以上方法都不會修改數組中的包含的值。

其中,every()filter()方法最相似。

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

var everyResult = numbers.every(function(item, index, array) {
    return (item > 2);
});

alert(everyResult);     //false

var someResult = numbers.some(function(item, index, array) {
    return (item > 2);
});

alert(someResult);      //true

filter()簡稱爲濾波器,作用也有點類似濾波器。可以用來過濾出符合條件項。

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

var filterResult = numbers.filter(function(item, index, array) {
    return (item > 2);
});

alert(filterResult);            //[3, 4, 5, 4, 3]

map()可以用來創建包含的項與另一個數組一一對應的項。

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

var mapResult = numbers.filter(function(item, index, array) {
    return item * 2;
});

alert(mapResult);           //[2, 4, 6, 8, 10, 8, 6, 4, 2]

forEach()本質上和使用for循環迭代數組一樣。

var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

numbers.forEach(function(item, index, array) {
    //執行某些操作
});

歸併方法

ECMAScript5還新增了兩個歸併數組的方法:reduce()reduceRight()。這兩個方法都會迭代數組的所有項,然後構建一個最終返回的值。reduce()從前到後,reduceRight()從後到前。
這兩個方法都接收兩個參數:一個在每一項上調用的函數和(可選的)作爲歸併基礎的初始值。傳給reduce()reduceRight()的函數接收4個參數:前一個值、當前值、項的索引和數組對象。這個函數返回的任何值都會作爲第一個參數自動傳給下一項。第一次迭代發生在數組的第二項上,因此第一個參數是數組的第一項,第二個參數就是數組的第二項。
使用reduce()方法可以執行數組中所有值求和操作。

var values = [1, 2, 3, 4, 5];
var sum = values.reduce(function(prev, cur, index, array) {
    return prev + cur;
});
alert(sum);     //15

Date類型

ECMAScript中的Date類型是在早期Java中的java.util.Date類基礎上構建的。爲此,Date類型使用自UTC(Coordinated Universal Time,國際協調時間)1970年1月1日午夜(零時)開始經過的毫秒數來保存日期。

創建日期對象,使用new操作符和Date構造函數即可。

var now = new Date()

無參數傳遞時自動獲取系統當前日期和時間。如果想根據特定日期時間創建日期對象,傳入該日期毫秒數。爲簡化計算過程,ECMPScript提供兩個方法:Date.parse()Date.UTC()
其中, Date.parse()接收一個表示日期的字符串。ECMA-262沒有定義 Date.parse()應該支持哪種日期格式,因此這個方法的行爲因實現而異,而且通過時因地區而異。將地區設置爲美國的瀏覽器通常都接受下列日期格式:

  • “月/日/年“,如6/13/2004;
  • “英文月名 日,年”,如January 12,2004;
  • “英文星期幾 英文月名 日 年 時:分:秒 時區”,如Tue May 25 2004 00:00:00 GMT-0700。
  • ISO 8601 擴展格式 YYYY-MM-DDTHH:mm:ss.sssZ(例如 2004-05-25T00:00:00)。只有兼容ECMAScript5的實現支持這種格式。

例如:

var someDate = new Date(Date.parse("May 25, 2004"));

如果傳入Date.parse()方法的字符串不能表示日期,那麼它會返回NaN。實際上,如果直接講字符串傳遞給Date構造函數,也會在後臺調用Date.parse()

Date.UTC()方法同樣也返回表示日期的毫秒數。 Date.UTC()的參數分別是年份、基於0的月份(0-11)、月中的哪一天、小時數(0-23)、分鐘、秒以及毫秒數。前兩個參數是必須的,其他默認爲0。

//GMT時間2000年1月1日午夜零時
var y2k = new Date(Date.UTC(2000, 0));

//GMT時間2005年5月5日下午5:55:55
var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));

與其他引用類型一樣,日期類型繼承了toLocaleString()toString()valueOf()方法。Date類型的toLocaleString()方法會按照與瀏覽器設置的地區相適應的格式返回日期和時間。valueOf()不返回字符串,而是返回日期的毫秒錶示。

Date類型還有一些專門用於將日期格式化爲字符串的方法,如下:

  • toDateString()——以特定於實現的格式顯示星期幾、月、日和年;
  • toTimeString()——以特定於時間的格式顯示時、分、秒和時區;
  • toLocaleDateString()——以特定於地區的格式顯示星期幾、月、日和年;
  • toLocaleTimeString()——以特定於實現的格式顯示時、分、秒;
  • toUTCString()——以特定於實現的格式顯示完整的UTC日期。

其他相關日期/時間組件方法,可參考w3cschool的JavaScript Date對象

RegExp對象

ECMAScript通過RegExp類型來支持正則表達式。

var expression = / pattern / flags ;

其中的模式(pattern)部分可以是任何簡單或複雜的正則表達式,可以包含字符類、限定符、分組、向前查找以及反向引用。每個正則表達式都可帶有一或多個標誌(flags),用以表明正則表達式的行爲。正則表達式匹配模式支持3個標誌。

  • g:表示全局模式(global),即模式將被用於所有字符串,而非在發現第一個匹配項是立即停止;
  • i:表示不區分大小寫(case-insensitive)模式,即在確定匹配項是忽略模式與字符串的大小寫;
  • m:表示多行(multiline)模式,即在到達一行文本末尾時還會繼續查找下一行中是否存在與模式匹配的項。

與其他語言中的正則表達式類似,模式中使用的所有元字符都必須轉義。
關於正則表達式的基本介紹,參考菜鳥教程上的正則表達式教程
另外,關於RegExp對象的介紹,可以參考w3cschool上的JavaScript RegExp對象。在這裏,就不贅述了。無論哪一門語言,在對字符串的處理上,正則表達式都是一個強大的工具,一定需要掌握。

Function類型

ECMAScript中最特殊的當屬函數了,因爲,函數實際上是對象!每個函數都是Function類型的實例,而且和其他引用類型一樣具有屬性和方法。由於函數是對象,因此函數名實際上也是一個指向函數對象的指針,不會與某個函數綁定。

函數聲明與函數表達式

解析器在向執行環境中加載數據時,對函數聲明和函數表達式並非一視同仁。解析器會率先讀取函數聲明,並使其執行代碼之前可用(可以訪問);至於函數表達式,則必須等到解析器執行到它所在的代碼行,纔會真正被解析執行。因此,除了什麼時候可以通過變量訪問函數這一點區別外,函數聲明與函數表達式的語法其實是等價的。

//使用函數聲明
alert(sum(10, 10));
function sum(num1, num2) {
    return num1 + num2;
}

上例能夠正常運行,因爲在代碼執行前,解析器就已經通過一個名爲函數聲明提升(function declaration hoisting)的過程,讀取並將函數聲明添加到執行環境中。對代碼求值時,JavaScript引擎在第一遍會聲明函數並將他們放到源代碼樹的頂部。所以,即使聲明函數的代碼在調用它的帶碼後面,JavaScript引擎也能把函數聲明提升到頂部。而下例函數表達式則不能運行

//使用函數表達式
alert(sum(10, 10));
var sum = function(num1, num2) {
    return num1 + num2;
}

沒有重載!
由於函數是對象,函數名實際上是一個指向函數對象的指針,不會與某個函數綁定,這正是ECMAScript中沒有函數重載概念的原因。個人理解,除此之外,由於ECMAScript中的變量爲鬆散類型,因此對於傳入函數的參數類型無法加以限制,因此無法像C++或者Java那樣根據傳入參數類型或者數量選擇調用函數,這也是造成ECMAScript無法重載的原因之一。

//函數聲明
function addSomeNumber(num) {
    return num + 100;
}

function addSomeNumber(num) {
    return num + 200;
}
var result = addSomeNumber(100);    //300

該例聲明兩個同名函數,結果是後面的函數覆蓋了前面的。該例與下面的代碼幾乎沒有區別:

var addSomeNumber = function (num) {
    return num + 100;
};

addSomeNumber = function (num) {
    return num + 200;
};

var result = addSomeNumber(100);        //300

作爲返回值的函數
由於ECMAScript中的函數名本身就是變量,因此函數也可以作爲值來使用。即可以作爲參數或者返回值。

//函數作爲參數
function callSomeFunction (someFunction, someArgument) {
    return somFunction (someArgument);
}

function add10 (num) {
    return num + 10;
}

var result1 = callSomeFunction (add10, 10);
alert(result1);     //20

function getGreeting (name) {
    return "Hello, " + name;
}

var result2 = callSomeFunction (getGreeting, "Nicholas");
alert(result2);     //"Hello, Nicholas"

函數作爲返回值是極有用的技術,是“閉包”技術的基礎之一。
比較典型的如數組sort()方法的比較函數,它需要接收兩個參數,比較它們的值。可以使假設有一個對象數組,要根據某個對象屬性排序,怎麼辦呢?

function createComparisonFunction (propertyName) {
    return function (object1, object2) {
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];

        if (value1 < value2) {
            return -1;
        } else if (value1 > value2) {
            return 1;
        } else {
            return 0;
        }
    };
}

該函數接收一個屬性名,然後根據這個屬性名來創建一個比較函數。使用如下

var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}];

data.sort(createComparisonFunction("name"));
alert(data[0].name);        //Nicholas

data.sort(createComparisonFunction("age"));
alert(data[0].name);        //Zachary

函數內部屬性(重點)
在函數內部,有兩個特殊的對象:argumentsthis

arguments的主要用途是保存函數參數,這個對象還有一個名叫callee的屬性,該屬性是一個指針,指向擁有這個arguments對象的函數。它可以完成函數體與函數名的解耦,如下面這個階乘函數的應用:

//與函數名緊緊耦合
function factorial (num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * factorial (num - 1)
    }
}

//使用arguments.callee替代函數名,消除耦合
function factorial (num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * arguments.callee (num - 1)
    }
}

這樣,無論引用函數時使用的是什麼名字,都可以保證正常完成遞歸調用。如

var trueFactorial = factorial;

factorial = function () {
    return 0;
};
alert(trueFactiorial(5));           //120
alert(factorial(5));                    //0

函數內部另一個特殊對象是this,其行爲與Java和C#中的this大致類似。即,this引用的是函數據以執行的環境對象——或者也可以說是this值(當在網頁的全局作用域中調用函數時,this對象引用的就是window)。如下例

window.color = "red";
var o = { color: "blue"};

function sayColor() {
    alert(this.color);
}

sayColor();             //"red"

o.sayColor = sayColor;
o.sayColor();           //"blue"

sayColor()在全局域中定義,當在全局域中調用時,this引用的是全局對象window;當把這個函數賦給對象o並調用o.sayColor()是,this引用的是對象o

ECMAScript 5也規範化了另一個函數對象的屬性:caller。這個屬性保存着調用當前函數的函數的引用,如果是在全局域中調用當前函數,它的值爲null。使用方式類似於callee,在此不贅述。

函數屬性和方法(重點)
ECMAScript中函數是對象,因此也有屬性和方法。

每個函數都包含兩個屬性:lengthprototype。其中,length表示函數希望接收的命名參數的個數;對於ECMAScript中的引用類型來說,prototype是保存他們所有實例方法的真正所在。
諸如toString()valueOf()等方法實際上都保存在prototype名下,只不過是通過各自對象的實例訪問它們罷了。在創建自定義引用類型以及實現繼承時,prototype屬性的作用極爲重要。在ECMAScript 5中,prototype屬性是不可枚舉的,因此使用for-in無法發現。

每個函數都包含兩個非繼承而來的方法:apply()call()。這兩個方法都是在特定的作用域中調用函數,實際上等於設置函數體內this對象的值。
apply()方法接收兩個參數,一個是在其中運行函數的作用域,另一個是參數數組。其中,第二個參數可以是Array的實例,也可以是arguments對象。例如

function sum(num1, num2) {
    return num1 + num2;
}
function callSum1(num1, num2) {
    return sum.apply(this, arguments);          //傳入arguments對象
}
function callSum2(num1, num2) {
    return sum.apply(this, [num1, num2]);       //傳入數組
}

alert(callSum1(10, 10));        //20
alert(callSum2(10, 10));        //20

call()方法與apply()方法的作用相同,他們的區別僅在於接收參數的方式不同。對於call()方法而言,第一個參數是this值沒有變化,變化的是其餘參數都直接傳遞給函數。即,在使用call()方法時,傳遞給函數的參數必須逐個列舉出來,如下:

function sum(num1, num2) {
    return num1 + num2;
}
function callSum(num1, num2) {
    return sum.call(this, num1, num2);
}

alert(callSum(10, 10));     //20

事實上,傳遞參數並非apply()call()真正用武之地,它們真正強大的地方是能夠擴充函數賴以運行的作用域。例如

window.color = "red";
var o = { color: "blue" };

function sayColor() {
    alert(this.color);
}

sayColor();                     //red

sayColor.call(this);            //red
sayColor.call(window);          //red
sayColor.call(o);               //blue

ECMAScript 5還定義了一個方法:bind()。這個方法會創建一個函數的實例,其this值會被綁定到傳給bind() 函數的值。例如

window.color = "red";
var o = { color: "blue" };

function sayColor() {
    alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor();           //blue

基本包裝類型

和Java類似,爲了便於操作基本類型值,ECMAScript也提供了3個特殊的引用類型:BooleanNumberString。每當讀取一個基本類型值的時候,後臺就會創建一個對應的基本包裝類型的對象,從而讓我們能夠調用一些方法來操作這些數據。有點類似於Java的自動拆裝箱過程,以String類型爲例

var s1 = "some text";
var s2 = s1.substring(2);

在訪問s1時,訪問過程處於讀取模式,後臺自動完成下列處理:

  1. 創建String類型的一個實例;
  2. 在實例上調用指定的方法;
  3. 銷燬這個實例。

以上三個步驟可以想象成下列代碼

var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;

以上三個步驟同樣適用於BooleanNumber類型對應的布爾值和數字值。
引用類型與基本包裝類型主要區別就是對象的生存期。使用new操作符創建的引用類型的實例,在執行流離開當前引用域之前一直都保存在內存中。而自動創建的基本包裝類型的對象,則只存在一行代碼的執行瞬間,然後立即銷燬。這意味着不能在運行時爲基本類型值添加屬性和方法。

Boolean類型

Boolean類型是與布爾值對應的引用類型,建議永遠不要使用Boolean對象。

Number類型

除了繼承的方法外,Number類型還提供了一些用於將數值格式化爲字符串的方。
- toFixed():按指定小數位返回數值的字符串表示。
- toExponential():返回以指數表示法表示的數值的字符串形式。
- toPrecision():返回固定大小格式,也可能返回指數格式,具體規則是看哪種格式比較合適。該方法接收一個參數指定表示數值的所有數字的位數。

String類型

String類型提供了很多方法,用於輔助完成對ECMAScript中字符串的解析和操作。

字符方法
兩個用於訪問字符串中特定字符的方法時:charAt()charCodeAt()。兩個方法都接收一個參數,即基於0餓字符位置。charAt()返回指定位置字符,charCodeAt()返回指定位置字符編碼。

字符串操作方法
concat()
用於將一或多個字符串拼接起來,接受任意多個參數。

slice()substr()substring()
這三個方法基於字符串創建新字符串。它們都會返回被操作字符串的一個子字符串,而且也都接受一或兩個參數。第一個參數指定子字符串的開始位置,第二個參數(在指定的情況下)表示子字符串到哪裏結束。具體來說,slice()substr()的第二個參數指定的是子字符串最後一個字符後面的位置。而substr()的第二個參數指定的則是返回的字符數。如果沒有給這些方法傳遞第二個參數,則將字符串的長度作爲結束位置。

var StringObject = "hello world";
alert(StringObject .slice(3));              //輸出 "lo world"
alert(StringObject .substring(3));          //輸出 "lo world"
alert(StringObject .substr(3));             //輸出 "lo world"
alert(StringObject .slice(3, 7));           //輸出 "lo w"
alert(StringObject .substring(3, 7));       //輸出 "lo w"
alert(StringObject .substr(3, 7));          //輸出 "lo worl"

當傳遞的參數是負值時,它們的行爲就不盡相同了。其中,slice()方法會將傳入的賦值與字符串長度相加,substr()方法將負的第一個參數加上字符串的長度,而將負的第二個參數轉換爲0.最後,substring()方法會把所有負值參數都轉換爲0.

var StringObject = "hello world";
alert(StringObject .slice(-3));             //輸出 "rld"
alert(StringObject .substring(-3));         //輸出 "hello world"
alert(StringObject .substr(-3));            //輸出 "rld"
alert(StringObject .slice(3, -4));          //輸出 "lo w"
alert(StringObject .substring(3, -4));      //輸出 "hel"
alert(StringObject .substr(3, -4));         //輸出 "" 空字符串

字符串位置方法
有兩個可以從字符串中查找子字符串的方法:indexOf()lastIndexOf()。前者從前往後搜索,後者反之。
兩個方法都可接收可選的第二個參數,表明從字符串哪個位置開始搜索。
在使用第二個參數的情況下,可以通過循環調用indexOf()lastIndexOf()來找到所有匹配的子字符串。如下:

var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = stringValue.indexOf("e");

while (pos > -1) {
    positions.push(pos);
    pos = stringValue.indexOf("e", pos + 1);
}

alert(positions);       //"3, 24, 32, 35, 52"

trim()方法
該方法創建一個字符串的副本,刪除前置及後綴的所有空格,然後返回結果。

字符串大小寫轉換方法
ECMAScript中涉及字符串大小寫轉換的方法有4個:toLowerCase()toLocaleLowerCase()toUpperCase()toLocaleUpperCase()

字符串的模式匹配方法

match()
在字符串上調用這個方法,本質上與調用RegExpexec()方法相同。match()只接受一個參數,正則表達式或者RegExp對象。和RegExp對象的exec()方法一樣,match()方法會返回一個數組:數組的第一項是與整個模式匹配的字符串,之後的每一項(如果有)保存着與正則表達式中的捕獲組匹配的字符串。

var text = "cat, bat, sat, fat";
var pattern = /.at/;

//與pattern.exec(text)相同
var matches = text.match(pattern);
alert(matches.index);           //0
alert(matches[0]);              //"cat"
alert(pattern.lastIndex);       //0



search()
參數與match()方法相同,返回字符串中第一個匹配項的索引,如果沒有返回-1。

var text  = "cat, bat, sat, fat";
var pos = text.search(/at/);
alert(pos);     //1



replace()
接受兩個參數,第一個參數可以是一個RegExp對象或者一個字符串(這個字符串不會被轉換爲正則表達式),第二個參數可以是一個字符串或者一個函數。如果第一個參數是字符串,那麼只會替換第一個字符串,想替換所有字符串只能提供一個正則表達式,並且要指定全局(g)標誌。

var text = "cat, bat, sat, fat";
var result = text.replace("at", "ond");
alert(result);      //"cond, bat, sond, fond"

如果第二個參數是字符串,那麼還可以使用一些特殊的字符序列,將正則表達式操作得到的值插入到結果字符串中。下表列出了ECMAScript提供的這些特殊的字符序列。

字符序列 替換文本
$$ $
$& 匹配整個模式的子字符串。與RegExp.lastMatch的值相同
$’ 匹配的子字符串之前的子字符串。與RegExp.leftContext的值相同
$` 匹配的子字符串之前的子字符串。與RegExp.rightContext的值相同
$n 匹配第n個捕獲組的子字符組,其中nn等於0~9。例如,$1是匹配第一個捕獲組的子字符串,$2是匹配第二個捕獲組的子字符串,以此類推。如果正則表達式中沒有定義捕獲組,則使用空字符串
$nn 匹配第nn個捕獲組的子字符串,其中nn等於01~99。例如$01是匹配第一個捕獲組的子字符串,$02是匹配第二個捕獲組的子字符串,以此類推。如果正則表達式中沒有定義捕獲組,則使用空字符串

通過這些特殊的字符序列,可以使用最近一次匹配結果中的內容,如下

var text = "cat, bat, sat, fat";
result = text.replace(/(.at)/g, "word ($1)");
alert(result);          //word (cat), word (bat), word(sat), word (fat)

replace()方法的第二個參數也可以是一個函數。在只有一個匹配項(即與模式匹配的字符串)的情況下,會向這個函數傳遞3個參數:模式的匹配項、模式匹配項在字符串中的位置和原始字符串。在正則表達式定義了多個捕獲組的情況下,傳遞給函數的參數一次是模式的匹配項、第一個捕獲組的匹配項、第二個捕獲組的匹配項……,但最後兩個參數仍然分別是模式的匹配項在字符串中的位置和原始字符串。這個函數應該返回一個字符串,表示應該被替換的匹配項使用函數作爲replace()方法的第二個參數可以實現更加精細的替換操作,如下例

\\返回html實體
function htmlEscape(text) {
    return text.replace(/[<>"&]/g, function(match, pos, originalText) {
        switch(match) {
            case "<":
                return "&lt;";
            case ">":
                return "&gt;";
            case "&":
                return "&amp;";
            case "\"":
                return "&quot;"
        }
    });
}

alert(htmlEscape("<p class=\"greeting\">Hello world!</p>"));
//&lt;p class=&quot;greeting&quot;&gt;Hello world!&lt;/p&gt;


split()
該方法可以基於指定的分隔符將一個字符串分割成多個子字符串,並將結果放在一個數組中。分隔符可以是字符串,也可以是一個RegExp對象(這個方法不會將字符串看成正則表達式)。split()方法可以接受可選的第二個參數,用於指定數組的大小,以便確保返回的數組不會超過既定大小。

var colorText = "red, blue, green, yellow";
var colors1 = colorText.split(",");         //["red", "blue", "green", "yellow"]
var colors2 = colorText.split(",", 2);      //["red", "blue"]
var colors3 = colorText.split(/[^\,]+/);    //["", ",", ",", ",", ""]

localeCompare()方法
該方法比較兩個字符串,並返回下列值中的一個:

  • 如果字符串在字母表中應該排在字符串參數之前,則返回一個負數(大多數情況下是-1,具體的值要視實現而定);
  • 如果字符串等於字符串參數,則返回0;
  • 如果字符串在字母表中應該排在字符串參數之後,則返回一個正數(大多數情況下是1,具體的值同樣要使實現而定)。
var stringValue = "yellow";
alert(stringValue.localeCompare("brick"));          //1
alert(stringValue.localeCompare("yellow"));     //0
alert(stringValue.localeCompare("zoo"));            //-1

fromCharCode()方法
String構造函數本身還有一個靜態方法:fromCharCode()。這個方法的任務是接收一或多個字符編碼,然後將它們轉換成一個字符串。從本質上講,這個方法與實例方法charCodeAt()執行的是相反的操作。

alert(String.fromCharCode(104, 101, 108, 108, 111); //"hello"

HTML方法(略)

單體內置對象

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

Global對象

Global(全局)對象可以說是ECMAScript中最特別的一個對象了,因爲 不管從什麼角度看,這個對象都是不存在的。ECMAScript中的Global對象在某種意義上是作爲一個終極的“兜底兒對象”來定義的。換句話說,不屬於任何其他對象的屬性和方法,最終都是它的屬性和方法。事實上,沒有全局變量或全局函數;所有在全局作用域中定義的屬性和函數,都是Global對象的屬性。前面介紹過的isNaN()
isFinite()parseInt()以及parseFloat(),實際上都是Global對象的方法。除此之外,Global對象還包含其他一些方法。

URI編碼方法
Global對象的encodeURI()encodeURIComponent()方法可以對URI(Uniform Resource Indentifiers,通用資源標識符)進行編碼,以便發送給瀏覽器。與這兩個方法對象的兩個方法分別是decodeURI()decodeURIComponent()。其中,decodeURI()只能對使用encodeURI()替換的字符進行解碼。

//編碼
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));

//解碼
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));

eval()方法
eval()是ECMAScript中非常強大的一個方法。它只接受一個參數,即要執行的ECMAScript(或JavaScript)字符串。
當解析器發現代碼中調用eval()方法時,它會將傳入的參數當作實際的ECMAScript語句來解析,然後把執行結果插入到原位置。通過eval()執行的代碼被認爲是包含該次調用的執行環境的一部分,因此被執行的代碼具有與該執行環境相同的作用域鏈。這意味着通過eval()執行的代碼可以引用在包含環境中定義的變量,例如:

var msg = "hello world";
eval("alert(msg)");     //"hello world"

變量msgeval()調用的環境之外定義,但其中調用的alert()仍然能夠顯示”hello world”。這是因爲上面第二行代碼最終被替換成了一行真正的代碼。同樣地,我們也可以在eval()調用中定義一個函數,然後再在該調用的外部代碼中引用這個函數:

eval("function sayHi () { alert('hi'); }");
sayHi();

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

eval("var msg = 'hello world'; ");
alert(msg);         //"hello world"

eval()中創建的任何變量或函數都不會被提升,因爲在解析代碼的時候,它們被包含在一個字符串中;它們旨在eval()執行的時候創建。
嚴格模式下,在外部訪問不到eval()中創建的任何變量或函數,因此前面兩個例子都會導致錯誤。同樣,在嚴格模式下,爲eval()賦值也會導致錯誤:

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

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對象
ECMAScript 雖然沒有指出如何直接訪問Global對象,但Web瀏覽器都是將這個全局對象作爲window對象的一部分加以實現的。因此,在全局作用域中聲明的所有變量和函數,就都成爲了window對象的屬性。

另一種取得Global對象的方法是使用以下代碼:

var global = function() {
    return this;
} ();

Math()對象

ECMAScript還爲保存數學公式和信息提供了一個公共位置,即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的平方根


Math對象的方法
min()max()確定一組數值中的最小值和最大值,可接受任意多個數值參數。例如:

//接收數值參數
var max  = Math.max(3, 54, 32, 16);
alert(max);     //54

var min = Math.min(3, 54, 32, 16);
alert(min);     //3

//接收數組參數,技巧在於把Math對象作爲apply()的第一個參數,從而正確設置this值,然後將任何數組作爲第二個參數。
var values = [1, 2, 3, 4, 5, 6, 7, 8];
var max = Math.max.apply(Math, values);

Math對象有三個將小數值舍入爲整數的幾個方法:Math.ceil()Math.floor()Math.round()

Math.random()方法返回大於等於0小於1的一個隨機數。套用下面的公式,就可以利用Math.random()從某個整數範圍內隨機選擇一個值。

值 = Math.floor(Math.random() * 可能值的總數 + 第一個可能的值)

例如

//選擇一個介於2到10之間的值
var num = Math.floor(Math.random() * 9 + 2);

以下函數可以直接指定隨機範圍(整數)

function selectFrom(lowerValue, upperValue) {
    var choices = upperValue - lowerValue +1;
    return Math.floor(Math.random() * choices + lowerValue);
}

var num = selectFrom(2, 10);
alert(num);     //介於2和10之間(包括2和10)的一個數值

其他方法

方法 說明 方法 說明
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的正切值
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章