JavaScript 高級(jí)程序設(shè)計(jì) 筆記二

第5章 引用類型

引用類型的值(對(duì)象)是引用類型的一個(gè)示例。在ECMAScript 中,引用類型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)據(jù)和功能組織在一起。

對(duì)象是某個(gè)特定引用類型的實(shí)例。新對(duì)象是使用new操作符后跟一個(gè)構(gòu)造函數(shù)來(lái)創(chuàng)建的。構(gòu)造函數(shù)本身就是一個(gè)函數(shù),只不過(guò)該函數(shù)是出于創(chuàng)建新對(duì)象的目的而定義的。

Object 類型

創(chuàng)建Object 實(shí)例的方式有兩種。第一種是使用new 操作符后跟Object 構(gòu)造函數(shù)。

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

另一種方式是使用對(duì)象字面量表示法。對(duì)象字面量是對(duì)象定義的一種簡(jiǎn)寫(xiě)形式,目的在于簡(jiǎn)化創(chuàng)建包含大量屬性的對(duì)象的過(guò)程。

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

在這個(gè)例子中,左邊的花括號(hào){表示對(duì)象字面量的開(kāi)始,因?yàn)樗霈F(xiàn)在了表達(dá)式上下(expression context)文中。ECMAScript 中的表達(dá)式上下文指的是該上下文期待一個(gè)值(表達(dá)式)。賦值操作符表示后面是一個(gè)值,所以左花括號(hào)在這里表示一個(gè)表達(dá)式的開(kāi)始。同樣的花括號(hào),如果出現(xiàn)在一個(gè)語(yǔ)句上下文(statement context)中,例如跟在if 語(yǔ)句條件的后面,則表示一個(gè)語(yǔ)句塊的開(kāi)始。

在使用對(duì)象字面量語(yǔ)法時(shí),屬性名也可以使用字符串。如果留空其花括號(hào),則可以定義只包含默認(rèn)屬性和方法的對(duì)象。

對(duì)象字面量也是向函數(shù)傳遞大量可選參數(shù)的首選方式。

訪問(wèn)對(duì)象屬性時(shí)使用的是點(diǎn)表示法。不過(guò),在JavaScript 也可以使用方括號(hào)表示法來(lái)訪問(wèn)對(duì)象的屬性。在使用方括號(hào)語(yǔ)法時(shí),應(yīng)該將要訪問(wèn)的屬性以字符串的形式放在方括號(hào)中。方括號(hào)語(yǔ)法的主要優(yōu)點(diǎn)是可以通過(guò)變量來(lái)訪問(wèn)屬性。如果屬性名中包含會(huì)導(dǎo)致語(yǔ)法錯(cuò)誤的字符,或者屬性名使用的是關(guān)鍵字或保留字,也可以使用方括號(hào)表示法。通常,除非必須使用變量來(lái)訪問(wèn)屬性,否則建議使用點(diǎn)表示法。

Array 類型

ECMAScript 中的數(shù)組與其他多數(shù)語(yǔ)言中的數(shù)組有著相當(dāng)大的區(qū)別。ECMAScript 數(shù)組的每一項(xiàng)可以保存任何類型的數(shù)據(jù)。且ECMAScript 數(shù)組的大小是可以動(dòng)態(tài)調(diào)整的。

創(chuàng)建數(shù)組的基本方式有兩種。第一種是使用Array 構(gòu)造函數(shù),如:

var colors = new Array()

第二種方式是使用數(shù)組字面量表示法。數(shù)組字面量由一對(duì)包含數(shù)組項(xiàng)的方括號(hào)表示,多個(gè)數(shù)組項(xiàng)之間以逗號(hào)隔開(kāi)。如:

var colors = ["red","blue","green"];

數(shù)組的length 屬性很有特點(diǎn)——它不是只讀的。因此,通過(guò)設(shè)置這個(gè)屬性,可以從數(shù)組的末尾移除項(xiàng)或向數(shù)組中添加新項(xiàng)。如果將其length 屬性設(shè)置為大于數(shù)組項(xiàng)數(shù)的值,則新增的每一項(xiàng)都會(huì)取得undefined 值。

檢測(cè)數(shù)組

對(duì)于一個(gè)網(wǎng)頁(yè),或者一個(gè)全局作用域而言,使用instanceof 操作符即可:

if (value instanceof Array){
  // 對(duì)數(shù)組執(zhí)行某些操作
}

ECMAScript 5 新增了Array.isArray()方法。這個(gè)方法的目的是最終確定某個(gè)值到底是不是數(shù)組,而不管它是在哪個(gè)全局執(zhí)行環(huán)境中創(chuàng)建的。用法如下:

if (Array.isArray(value)){
  // 對(duì)數(shù)組執(zhí)行某些操作
}

轉(zhuǎn)換方法

調(diào)用數(shù)組的toString()方法會(huì)返回由數(shù)組中每個(gè)值得字符串形式拼接而成的一個(gè)以逗號(hào)分隔的字符串。而調(diào)用valueOf()返回的還是數(shù)組。

toLocaleString()方法經(jīng)常也會(huì)返回與toString()valueOf()方法相同的值,但也不總是如此。

以上三種方法,在默認(rèn)情況下都會(huì)以逗號(hào)分隔的字符串的形式返回?cái)?shù)組項(xiàng)。而如果使用join()方法,則可以使用不同的分隔符來(lái)構(gòu)建這個(gè)字符串。join()方法只接收一個(gè)參數(shù),即用作分隔符的字符串,然后返回包含所有數(shù)組項(xiàng)的字符串。

var colors = ["red","green","blue"];
alert(colors.join(",")) //red,green,blue
alert(colors.join("||"))  //red||green||blue

如果不給join()方法傳入任何值,或者給它傳入undefined,則使用逗號(hào)作為分隔符。

棧方法

數(shù)組可以表現(xiàn)的就像棧一樣,后者是一種可以限制插入和刪除項(xiàng)的數(shù)據(jù)結(jié)構(gòu)。棧是一種 LIFO(Last-In-First-Out,后進(jìn)先出)的數(shù)據(jù)結(jié)構(gòu),也就是最新添加的項(xiàng)最早被移除。而棧中項(xiàng)的插入(叫做推入)和移除(叫做彈出),只發(fā)生在一個(gè)位置——棧的頂部。ECMAScript 為數(shù)組專門(mén)提供了push()pop()方法,以便實(shí)現(xiàn)類似棧的行為。

push()方法可以接受任意數(shù)量的參數(shù),把它們逐個(gè)添加到數(shù)組末尾,并返回修改后數(shù)組的長(zhǎng)度。而pop()方法則從數(shù)組末尾移除最后一項(xiàng),減少數(shù)組的length值,然后返回移除的項(xiàng)。

隊(duì)列方法

隊(duì)列數(shù)據(jù)結(jié)構(gòu)的訪問(wèn)規(guī)則是FIFO(First-In-First-Out,先進(jìn)先出)。隊(duì)列在列表的末端添加項(xiàng),從列表的前端移除項(xiàng)。shift()方法可以移除數(shù)組中的第一個(gè)項(xiàng)并返回該項(xiàng),同時(shí)數(shù)組長(zhǎng)度減1。結(jié)合使用shift()push()方法,可以像使用隊(duì)列一樣使用數(shù)組。

unshift()方法能在數(shù)組前端添加任意個(gè)項(xiàng)并返回新數(shù)組的長(zhǎng)度。同時(shí)使用unshift()pop()方法,可以從相反的方向來(lái)模擬隊(duì)列,即在數(shù)組的前端添加項(xiàng),從數(shù)組末端移除項(xiàng)。

重排序方法

reverse()方法會(huì)反轉(zhuǎn)數(shù)組項(xiàng)的順序。

在默認(rèn)情況下,sort()方法按升序排列數(shù)組項(xiàng)——即最小的值位于最前面,最大的值排在最后面。為了實(shí)現(xiàn)排序,sort()方法會(huì)調(diào)用每個(gè)數(shù)組項(xiàng)的toString()轉(zhuǎn)型方法,然后比較得到的字符串,以確定如何排序。即使數(shù)組中的每一項(xiàng)都是數(shù)值,sort()方法比較的也是字符串。

sort()方法可以接收一個(gè)比較函數(shù)作為參數(shù),以便指定哪個(gè)值位于哪個(gè)值前面。比較函數(shù)接受兩個(gè)參數(shù),如果第一個(gè)參數(shù)應(yīng)該位于第二個(gè)之前則返回一個(gè)負(fù)數(shù),如果兩個(gè)參數(shù)相等則返回0,如果第一個(gè)參數(shù)應(yīng)該位于第二個(gè)之后則返回一個(gè)正數(shù)。

操作方法

concat()方法可以基于當(dāng)前數(shù)組中的所有項(xiàng)創(chuàng)建一個(gè)新數(shù)組。具體來(lái)說(shuō),這個(gè)方法會(huì)先創(chuàng)建當(dāng)前數(shù)組的一個(gè)副本,然后將接收到的參數(shù)添加到這個(gè)副本的末尾,最后返回新構(gòu)建的數(shù)組。在沒(méi)有給concat()方法傳遞參數(shù)的情況下,它只是復(fù)制當(dāng)前數(shù)組并返回副本。

slice()能夠基于當(dāng)前數(shù)組中的一或多個(gè)項(xiàng)創(chuàng)建一個(gè)新數(shù)組。slice()方法可以接受一或兩個(gè)參數(shù),即要返回項(xiàng)的起始和結(jié)束為止。在只有一個(gè)參數(shù)的情況下,該方法返回從該參數(shù)指定為止開(kāi)始到當(dāng)前數(shù)組末尾的所有項(xiàng)。 slice()方法不會(huì)影響原始數(shù)組。

如果slice()方法的參數(shù)中有一個(gè)負(fù)數(shù),則用數(shù)組長(zhǎng)度加上該數(shù)來(lái)確定相應(yīng)的位置。如果結(jié)束位置小于起始位置,則返回空數(shù)組。

splice()的主要用途是向數(shù)組的中部插入項(xiàng),方法:

  • 刪除:可以刪除任意數(shù)量的項(xiàng),只需指定2個(gè)參數(shù):要?jiǎng)h除的第一項(xiàng)的位置和要?jiǎng)h除的項(xiàng)數(shù)。例如,splice(0,2)會(huì)刪除數(shù)組中的前兩項(xiàng)。
  • 插入:可以向指定位置插入任意數(shù)量的項(xiàng),只需提供3個(gè)參數(shù):起始位置、0(要?jiǎng)h除的項(xiàng)數(shù))和要插入的項(xiàng)。如果要插入多個(gè)項(xiàng),可以再傳入第四、第五,以至任意多個(gè)項(xiàng)。
  • 替換:可以向指定位置插入任意數(shù)量的項(xiàng),且同時(shí)刪除任意數(shù)量的項(xiàng),只需指定3個(gè)參數(shù):起始位置、要?jiǎng)h除的項(xiàng)數(shù)和要插入的任意數(shù)量的項(xiàng)。插入的項(xiàng)數(shù)不必與刪除的項(xiàng)數(shù)相等。

splice()方法始終都會(huì)返回一個(gè)數(shù)組,該數(shù)組包含從原始數(shù)組中刪除的項(xiàng)。

位置方法

  • indexOf()
  • lastIndexOf()

接收兩個(gè)參數(shù):要查找的項(xiàng)和表示查找起點(diǎn)位置的索引(可選)。

返回要查找的項(xiàng)在數(shù)組中的位置,或者在沒(méi)找到的情況下返回-1。查找時(shí)必須嚴(yán)格相等(===)。

迭代方法

ECMAScript 5 為數(shù)組定義了5個(gè)迭代方法。每個(gè)方法都接收兩個(gè)參數(shù):要在每一項(xiàng)上運(yùn)行的函數(shù)和運(yùn)行該函數(shù)的作用域?qū)ο蟆绊憈his 的值(可選)。傳入這些方法中的函數(shù)會(huì)接收三個(gè)參數(shù):數(shù)組項(xiàng)的值、該項(xiàng)在數(shù)組中的位置和數(shù)組對(duì)象本身。

  • every():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),如果該函數(shù)對(duì)每一項(xiàng)都返回true,則返回true
  • filter():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),返回該函數(shù)會(huì)返回true 的項(xiàng)組成的數(shù)組
  • forEach():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù)。這個(gè)方法沒(méi)有返回值
  • map():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),返回每次函數(shù)調(diào)用的結(jié)果組成的數(shù)組
  • some():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),只要有任一項(xiàng)返回true,則返回true

filter() 示例:

var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = number.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.map(function(item,index,array){
  return item * 2;
});
alert mapResult;  //[2,4,6,8,10,8,6,4,2]

歸并方法

  • reduce()
  • reduceRight()

這兩個(gè)方法都會(huì)迭代數(shù)組的所有項(xiàng),然后構(gòu)建一個(gè)最終返回的值。

這兩個(gè)方法都接收兩個(gè)參數(shù):一個(gè)在每一項(xiàng)上調(diào)用的函數(shù)和作為歸并基礎(chǔ)的初始值(可選)。傳給的函數(shù)接受4個(gè)參數(shù):前一個(gè)值、當(dāng)前值、項(xiàng)的索引和數(shù)組對(duì)象。這個(gè)函數(shù)返回的任何值都會(huì)作為第一個(gè)參數(shù)自動(dòng)傳給下一項(xiàng)。

使用reduce() 方法可以執(zhí)行求數(shù)組中所有值之和的操作,例如:

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

Date 類型

Date 類型使用自UTC 1970年1月1日零時(shí)開(kāi)始經(jīng)過(guò)的毫秒數(shù)來(lái)保存日期。

要?jiǎng)?chuàng)建一個(gè)日期對(duì)象,使用new 操作符和Date 構(gòu)造函數(shù)即可:

var now = new Date();

在調(diào)用Date 構(gòu)造函數(shù)而不傳遞參數(shù)的情況下,新創(chuàng)建的對(duì)象自動(dòng)獲得當(dāng)前日期和時(shí)間。

Date.parse()方法接收一個(gè)表示日期的字符串參數(shù),然后嘗試根據(jù)這個(gè)字符串返回相應(yīng)日期的毫秒數(shù)。

Date.UTC()方法同樣也返回表示日期的毫秒數(shù),參數(shù)分別是年份、基于0的月份、月中的那一天、小時(shí)數(shù)、分鐘、秒以及毫秒數(shù)。在這些參數(shù)中,只有前兩個(gè)參數(shù)是必需的。

ECMAScript 5 添加了Date.now()方法,返回表示調(diào)用這個(gè)方法時(shí)的日期和時(shí)間的毫秒數(shù)。這個(gè)方法簡(jiǎn)化了使用Date 對(duì)象分析代碼的工作。例如:

var start = Date.now();
doSomething();
var stop = Date.now();
var result = stop - start;

RegExp 類型

字面量形式:

var expression = / pattern / flags ;

flags:

  • g:表示全局模式global
  • i:表示不區(qū)分大小寫(xiě)模式ignoreCase
  • m:表示多行模式multiline

RegExp 構(gòu)造函數(shù):

var pattern = new RegExp("pattern","flags");

模式中使用的所有元字符都必須轉(zhuǎn)義。正則表達(dá)式中的元字符包括:

( [ { \ ^ $ | ) * + . ] }

RegExp 實(shí)例屬性

  • global:布爾值,表示是否設(shè)置了g 標(biāo)志
  • ignoreCase:布爾值,表示是否設(shè)置了i 標(biāo)志
  • lastIndex:整數(shù),表示開(kāi)始搜索下一個(gè)匹配項(xiàng)的字符位置,從0算起
  • multiline:布爾值,表示是否設(shè)置了m 標(biāo)志
  • source:正則表達(dá)式的字符串表示,按照字面量形式而非傳入構(gòu)造函數(shù)中的字符串模式返回

RegExp 實(shí)例方法

主要方法是exec(),該方法是專門(mén)為捕獲組而設(shè)計(jì)的。exec()接受一個(gè)參數(shù),即要應(yīng)用模式的字符串,然后返回包含第一個(gè)匹配項(xiàng)信息的數(shù)組;或者在沒(méi)有匹配項(xiàng)的情況下返回null。返回的數(shù)組雖然是Array 的實(shí)例,但包含兩個(gè)額外的屬性:index 和input。其中,index 表示匹配項(xiàng)在字符串中的位置,而input 表示應(yīng)用正則表達(dá)式的字符串。在數(shù)組中,第一項(xiàng)是與整個(gè)模式匹配的字符串,其他項(xiàng)是與模式中的捕獲組匹配的字符串(如果模式中沒(méi)有捕獲組,則該數(shù)組只包含一項(xiàng))。

對(duì)于exec() 方法而言,即使在模式中設(shè)置了全局標(biāo)志g ,它每次也只會(huì)返回一個(gè)匹配項(xiàng)。在不設(shè)置全局標(biāo)志的情況下,在同一個(gè)字符串上多次調(diào)用exec() 將始終返回第一個(gè)匹配項(xiàng)的信息。而在設(shè)置全局標(biāo)志的情況下,每次調(diào)用exec() 則都會(huì)在字符串中繼續(xù)查找新匹配項(xiàng)。

方法test()接受一個(gè)字符串參數(shù)。在模式與該參數(shù)匹配的情況下返回true;否則,返回false。此方法經(jīng)常用在if 語(yǔ)句中。

RegExp 構(gòu)造函數(shù)屬性

Function 類型

由于函數(shù)是對(duì)象,因此函數(shù)名實(shí)際上也是一個(gè)指向函數(shù)對(duì)象的指針,不會(huì)與某個(gè)函數(shù)綁定。

使用函數(shù)聲明語(yǔ)法定義:

function sum(num1,num2){
  return num1 + num2;
}

使用函數(shù)表達(dá)式定義:

var sum = function(num1,num2){
  return num1 + num2;
};

解析器在執(zhí)行環(huán)境中加載數(shù)據(jù)時(shí),對(duì)函數(shù)聲明和函數(shù)表達(dá)式并非一視同仁。解析器會(huì)率先讀取函數(shù)聲明,并使其在執(zhí)行任何代碼之前可用;至于函數(shù)表達(dá)式,則必須等到解析器執(zhí)行到它所在的代碼行,才會(huì)真正被解釋執(zhí)行。

因?yàn)镋CMAScript 中的函數(shù)名本身就是變量,所以函數(shù)也可以作為值來(lái)使用。也就是說(shuō),不僅可以像傳遞參數(shù)一樣把一個(gè)函數(shù)傳遞給另一個(gè)函數(shù),而且可以將一個(gè)函數(shù)作為另一個(gè)函數(shù)的結(jié)果返回。

函數(shù)內(nèi)部屬性

  • arguments
  • this

雖然arguments 的主要用途是保存函數(shù)參數(shù),但這個(gè)對(duì)象還有一個(gè)名叫callee的屬性,該屬性時(shí)一個(gè)指針,指向擁有這個(gè)arguments 對(duì)象的函數(shù)。

階乘函數(shù)示例,使用arguments.callee ,可消除緊密耦合的現(xiàn)象:

function factorial(num){
  if (num<=1){
    return 1;
  } else {
    return num*arguments.callee(num-1);
  }
}

this 引用的是函數(shù)執(zhí)行的環(huán)境對(duì)象。例如在網(wǎng)頁(yè)的全局作用域中調(diào)用函數(shù)時(shí),this 對(duì)象引用的就是window。

ECMAScript 5 也規(guī)范化了另一個(gè)函數(shù)對(duì)象的屬性:caller。這個(gè)屬性中保存著調(diào)用當(dāng)前函數(shù)的函數(shù)的引用,如果是在全局作用域中調(diào)用當(dāng)前函數(shù),它的值為null。

函數(shù)屬性和方法

ECMAScript 中的函數(shù)是對(duì)象,因此函數(shù)也有屬性和方法。每個(gè)函數(shù)都包含兩個(gè)屬性:length 和 prototype。

length 屬性表示函數(shù)希望接收的命名參數(shù)的個(gè)數(shù)。

每個(gè)函數(shù)都包含兩個(gè)非繼承而來(lái)的方法:apply() 和call()。這兩個(gè)方法的用途都是在特定的作用域中調(diào)用函數(shù),實(shí)際上等于設(shè)置函數(shù)體內(nèi)this 對(duì)象的值。

apply() 方法接收兩個(gè)參數(shù):一個(gè)是在其中運(yùn)行函數(shù)的作用域,另一個(gè)是參數(shù)數(shù)組。其中,第二個(gè)參數(shù)可以是Array 的實(shí)例,也可以是arguments 對(duì)象。

call() 方法接受的第一個(gè)參數(shù)this 值和上面相同,變化的是其余參數(shù)都直接傳遞給函數(shù)。換句話說(shuō),傳遞給函數(shù)的參數(shù)必須逐個(gè)列舉出來(lái)。

apply() 和call() 真正強(qiáng)大的地方是能夠擴(kuò)充函數(shù)賴以運(yùn)行的作用域,示例:

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

基本包裝類型

引用類型與基本包裝類型的主要區(qū)別就是對(duì)象的生存期。使用new 操作符創(chuàng)建的引用類型的實(shí)例,在執(zhí)行流離開(kāi)當(dāng)前作用域之前都一直保存在內(nèi)存中。而自動(dòng)創(chuàng)建的基本包裝類型的對(duì)象,則只存在于一行代碼的執(zhí)行瞬間,然后立即被銷毀。這意味著我們不能在運(yùn)行時(shí)為基本類型值添加屬性和方法。

Number 類型

toFixed() 方法會(huì)按照指定的小數(shù)位返回?cái)?shù)值的字符串表示,例如:

var num = 10;
alert(num.toFixed(2));  //"10.00"

toExponential() 方法返回以指數(shù)表示法表示的數(shù)值的字符串形式。

var num = 10;
alert(num.toExponentia(1)); //"1.0e+1"

String 類型

String 類型的每個(gè)實(shí)例都有一個(gè)length 屬性,表示字符串中包含多個(gè)字符。

兩個(gè)用于訪問(wèn)字符串中特定字符的方法是:charAt() 和 charCodeAt()。

var stringValue = "hello world";
alert(stringValue.charAt(1)); //"e"
alert(stringValue.charCodeAt(1)); //輸出"101"
alert(stringValue[1]);  //"e"

concat() 用于將一或多個(gè)字符串拼接起來(lái),返回拼接得到的新字符串。實(shí)踐中更多使用加號(hào)操作符(+)

var a = "hello";
var b = a.concat(" world");
var c = a.concat(" world","!");
alert(b); //"hello world"
alert(a); //"hello"
alert(c); //"hello world!"

基于子字符串創(chuàng)建新字符串的方法:

  • slice()
  • substr()
  • substring()

字符串位置方法:

  • indexOf()
  • lastIndexOf()

從一個(gè)字符串中搜索給定的子字符串,然后返回子字符串的位置,如果沒(méi)有找到該子字符串,則返回-1。

trim() 方法:創(chuàng)建一個(gè)字符串的副本,刪除前置及后綴的所有空格,然后返回結(jié)果。

大小寫(xiě)轉(zhuǎn)換方法:

  • toLowerCase()
  • toUpperCase()

字符串中匹配模式的方法:

  • match()
  • search()

替換方法replace():

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

split() 可以基于指定的分隔符將一個(gè)字符串分割成多個(gè)子字符串,并將結(jié)果放在一個(gè)數(shù)組中。

fromCharCode() 接收一或多個(gè)字符編碼,然后將它們轉(zhuǎn)換成一個(gè)字符串。從本質(zhì)上來(lái)看,這個(gè)方法與實(shí)例方法charCodeAt() 執(zhí)行的是相反的操作。

單體內(nèi)置對(duì)象

在所有代碼執(zhí)行之前,作用域中就已經(jīng)存在兩個(gè)內(nèi)置對(duì)象:Global 和Math。在大多數(shù)ECMAScript 實(shí)現(xiàn)中都不能直接訪問(wèn)Global 對(duì)象;不過(guò),Web 瀏覽器實(shí)現(xiàn)了承擔(dān)該角色的window 對(duì)象。全局變量和函數(shù)都是Global 對(duì)象的屬性。Math 對(duì)象提供了很多屬性和方法,用于輔助完成復(fù)雜的數(shù)學(xué)計(jì)算任務(wù)。

Global 對(duì)象

URI編碼方法:

  • encodeURI()
  • encodeURIComponent()
  • decodeURI()
  • decodeURIComponent()

eval() 方法就像是一個(gè)完整的ECMAScript 解析器,它只接受一個(gè)參數(shù),即要執(zhí)行的ECMAScript(或JavaScript)字符串。

當(dāng)解析器發(fā)現(xiàn)代碼中調(diào)用eval() 方法時(shí),它會(huì)將傳入的參數(shù)當(dāng)做實(shí)際的ECMAScript 語(yǔ)句來(lái)解析,然后把執(zhí)行結(jié)果插入到原位置。通過(guò)eval() 執(zhí)行的代碼被認(rèn)為是包含該次調(diào)用的執(zhí)行環(huán)境的一部分,因此被執(zhí)行的代碼具有與該執(zhí)行環(huán)境相同的作用域鏈。這意味著通過(guò)eval() 執(zhí)行的代碼可以引用在包含環(huán)境定義的變量。

使用eval() 時(shí)必須謹(jǐn)慎,特別是在它執(zhí)行用戶輸入數(shù)據(jù)的情況下。否則,可能會(huì)有惡意用戶輸入威脅你的站點(diǎn)或應(yīng)用程序安全的代碼(代碼注入)。

Math 對(duì)象

Math 對(duì)象包含的屬性大都是數(shù)學(xué)計(jì)算中可能會(huì)用到的一些特殊值。

min() 和 max() 方法用于確定一組數(shù)值中的最小值和最大值。這兩個(gè)方法都可以接收任意多個(gè)數(shù)值參數(shù)。

如需要找到數(shù)組中的最大或最小值,可以使用apply() 方法

var values = [1,2,3,4,5];
var max = Math.max.apply(Math,values);

舍入方法:

  • Math.ceil() 執(zhí)行向上舍入
  • Math.floor() 執(zhí)行向下舍入
  • Math.round() 執(zhí)行標(biāo)準(zhǔn)舍入(四舍五入)

Math.random() 方法返回大于等于0小于1的一個(gè)隨機(jī)數(shù)。套用下面的公式,就可以利用Math.random() 從某個(gè)整數(shù)范圍內(nèi)隨機(jī)選擇一個(gè)值:

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

其他方法:

第6章 面向?qū)ο蟮某绦蛟O(shè)計(jì)

ECMAScript 中沒(méi)有類的概念,因此它的對(duì)象也與基于類的語(yǔ)言中的對(duì)象有所不同。

可以把ECMAScript 的對(duì)象想象成散列表:無(wú)非就是一組名值對(duì),其中值可以是數(shù)據(jù)或函數(shù)。

每個(gè)對(duì)象都是基于一個(gè)引用類型創(chuàng)建的。

理解對(duì)象

屬性類型

ECMAScript 有兩種屬性:數(shù)據(jù)屬性和訪問(wèn)器屬性。

數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置。在這個(gè)位置可以讀取和寫(xiě)入。數(shù)據(jù)屬性有4個(gè)描述其行為的特性。

  • [[Configurable]]:表示能否通過(guò)delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問(wèn)器屬性。
  • [[Enumerable]]:表示能否通過(guò)for-in 循環(huán)返回屬性。
  • [[Writable]]:表示能否修改屬性的值。
  • [[Value]]:包含這個(gè)屬性的數(shù)據(jù)值。讀取屬性值的時(shí)候,從這個(gè)位置讀;寫(xiě)入屬性值的時(shí)候,把新值保存在這個(gè)位置。這個(gè)特性的默認(rèn)值為undefined。

要修改屬性默認(rèn)的特性,必須使用ECMAScript 5 的Object.defineProperty() 方法。這個(gè)方法接收三個(gè)參數(shù):屬性所在的對(duì)象、屬性的名字和一個(gè)描述符對(duì)象。其中描述符對(duì)象的屬性必須是:configurable、enumerable、writable 和value。設(shè)置其中的一或多個(gè)值,可以修改對(duì)應(yīng)的特性值。

訪問(wèn)器屬性不包含數(shù)據(jù)值。有如下4個(gè)特性:

  • [[Configurable]]
  • [[Enuerable]]
  • [[Get]]:在讀取屬性時(shí)調(diào)用的函數(shù)。默認(rèn)值為undefined。
  • [[Set]]:在寫(xiě)入屬性時(shí)調(diào)用的函數(shù)。默認(rèn)值為undefined。

訪問(wèn)器屬性不能直接定義,必須使用Object.defineProperty() 來(lái)定義。

訪問(wèn)器屬性的常見(jiàn)使用方式:設(shè)置一個(gè)屬性的值會(huì)導(dǎo)致其他屬性發(fā)生變化。

定義多個(gè)屬性

由于為對(duì)象定義多個(gè)屬性的可能性很大,ECMAScript 5 又定義了一個(gè)Object.defineProperties() 方法。利用這個(gè)方法可以通過(guò)描述符一次定義多個(gè)屬性。這個(gè)方法接收兩個(gè)對(duì)象參數(shù):第一個(gè)對(duì)象是要添加和修改其屬性的對(duì)象,第二個(gè)對(duì)象的屬性與第一個(gè)對(duì)象中要添加或修改的屬性一一對(duì)應(yīng)。

讀取屬性的特性

使用ECMAScript 5 的Object.getOwnPropertyDescriptor() 方法,可以取得給定屬性的描述符。這個(gè)方法接收兩個(gè)參數(shù):屬性所在的對(duì)象和要讀取其描述符的屬性名稱。返回值是一個(gè)對(duì)象,如果是訪問(wèn)器屬性,這個(gè)對(duì)象的屬性有configurable、enumerable、get和set;如果是數(shù)據(jù)屬性,這個(gè)對(duì)象的屬性有configurable、enumerable、writable和value。

創(chuàng)建對(duì)象

用Object 構(gòu)造函數(shù)或?qū)ο笞置媪縿?chuàng)建單個(gè)對(duì)象的缺點(diǎn):使用同一個(gè)接口創(chuàng)建很多對(duì)象,會(huì)產(chǎn)生大量的重復(fù)代碼。

工廠模式

工廠模式抽象了創(chuàng)建具體對(duì)象的過(guò)程。考慮到在ECMAScript 中無(wú)法創(chuàng)建類,開(kāi)發(fā)人員就發(fā)明了一種函數(shù),用函數(shù)來(lái)封裝以特定接口創(chuàng)建對(duì)象的細(xì)節(jié),例如:

function createPerson(name, age, job){
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function(){
    alert(this.name);
  };
  return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

函數(shù)createPerson() 能夠根據(jù)接受的參數(shù)來(lái)構(gòu)建一個(gè)包含所有必要信息的Person 對(duì)象??梢詿o(wú)數(shù)次地調(diào)用這個(gè)函數(shù),而每次它都會(huì)返回一個(gè)包含三個(gè)屬性和一個(gè)方法的對(duì)象。工廠模式雖然解決了創(chuàng)建多個(gè)相似對(duì)象的問(wèn)題,但卻沒(méi)有解決對(duì)象識(shí)別的問(wèn)題(即怎么知道一個(gè)對(duì)象的類型)。

構(gòu)造函數(shù)模式

function Person(name, age, job){
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function(){
    alert(this.name);
  };
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

Person() 中的代碼與createPerson() 有存下下面不同:

  • 沒(méi)有顯式地創(chuàng)建對(duì)象
  • 直接將屬性和方法賦給了this 對(duì)象
  • 沒(méi)有return 語(yǔ)句

函數(shù)名Person 使用的是大寫(xiě)字母P。按照慣例,構(gòu)造函數(shù)始終都應(yīng)該以一個(gè)大寫(xiě)字母開(kāi)頭,而非構(gòu)造函數(shù)則應(yīng)該以一個(gè)小寫(xiě)字母開(kāi)頭。主要是為了區(qū)別于ECMAScript 中的其他函數(shù),因?yàn)闃?gòu)造函數(shù)本身也是函數(shù),只不過(guò)可以用來(lái)創(chuàng)建對(duì)象而已。

要?jiǎng)?chuàng)建Person 的新實(shí)例,必須使用new 操作符。以這種方式調(diào)用構(gòu)造函數(shù)實(shí)際上會(huì)經(jīng)歷以下4個(gè)步驟:

  1. 創(chuàng)建一個(gè)新對(duì)象
  2. 將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this 就指向了這個(gè)新對(duì)象)
  3. 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性)
  4. 返回新對(duì)象

此例的兩個(gè)對(duì)象都有constructor (構(gòu)造函數(shù))屬性,該屬性指向Person。

alert(person1.constructor == Person); //true

對(duì)象的constructor 屬性最初是用來(lái)標(biāo)識(shí)對(duì)象類型的。還可用instanceof 來(lái)檢測(cè)對(duì)象類型。

alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true

可看出通過(guò)上例創(chuàng)建的所有對(duì)象既是Object 的實(shí)例,也是Person 的實(shí)例。

創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來(lái)可以將它的實(shí)例標(biāo)識(shí)為一種特定的類型;而這正是構(gòu)造函數(shù)模式勝過(guò)工廠模式的地方。

構(gòu)造函數(shù)與其他函數(shù)的唯一區(qū)別,就在于調(diào)用它們的方式不同。不過(guò),構(gòu)造函數(shù)畢竟也是函數(shù),不存在定義構(gòu)造函數(shù)的特殊語(yǔ)法。任何函數(shù),只要通過(guò)new 操作符來(lái)調(diào)用,那它就可以作為構(gòu)造函數(shù);而任何函數(shù),如果不通過(guò)new 操作符來(lái)調(diào)用,那它跟普通函數(shù)也不會(huì)有什么兩樣。

構(gòu)造函數(shù)的缺點(diǎn):每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍。

原型模式

我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè)prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,而這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。prototype 就是通過(guò)調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對(duì)象實(shí)例的原型對(duì)象。使用原型對(duì)象的好處是可以讓所有對(duì)象實(shí)例共享它所包含的屬性和方法。換句話說(shuō),不必在構(gòu)造函數(shù)中定義對(duì)象實(shí)例的信息,而是可以將這些信息直接添加到原型對(duì)象中。示例:

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
  alert(this.name);
};
var person1 = new Person();
person1.sayName();  //"Nicholas"
var person2 = new Person();
person2.sayName();  //"Nicholas"
alert(person1.sayName == person2.sayName);  //true

實(shí)例中的[[Prototype]] 這個(gè)連接存在于實(shí)例與函數(shù)的原型對(duì)象之間,而不是存在于實(shí)例與構(gòu)造函數(shù)之間。實(shí)例中的指針僅指向原型,而不指向構(gòu)造函數(shù)。

alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Object.getPrototypeOf(person1) == Person.prototype);  //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"

每當(dāng)代碼讀取某個(gè)對(duì)象的某個(gè)屬性時(shí),都會(huì)執(zhí)行一次搜索,目標(biāo)是具體給定名字的屬性。搜索首先從對(duì)象實(shí)例本身開(kāi)始。如果在實(shí)例中找到了具有給定名字的屬性,則返回該屬性的值;如果沒(méi)有找到,則繼續(xù)搜索指針指向的原型對(duì)象,在原型對(duì)象中查找具有給定名字的屬性。如果在原型對(duì)象中找到了這個(gè)屬性,則返回該屬性的值。

當(dāng)為對(duì)象實(shí)例添加一個(gè)屬性時(shí),這個(gè)屬性就會(huì)屏蔽原型對(duì)象中保存的同名屬性;換句話說(shuō),添加這個(gè)屬性只會(huì)阻止我們?cè)L問(wèn)原型中的那個(gè)屬性,但不會(huì)修改那個(gè)屬性。使用delete 操作符可以完全刪除實(shí)例屬性,從而讓我們能夠重新訪問(wèn)原型中的屬性。

使用hasOwnProperty() 方法可以檢測(cè)一個(gè)屬性是存在于實(shí)例中,還是存在于原型中。這個(gè)方法只在給定屬性存在于對(duì)象實(shí)例中時(shí),才會(huì)返回ture。

alert(person1.hasOwnProperty("name"));  //false
person1.name = "Greg";
alert(person1.name);  //"Greg"——來(lái)自實(shí)例
alert(person1.hasOwnProperty("name")) //true

delete person1.name;
alert(person1.name);  //"Nicholas"——來(lái)自原型
alert(person1.hasOwnProperty("name"));  //false

in 操作符會(huì)在通過(guò)對(duì)象能夠訪問(wèn)給定屬性時(shí)返回ture,無(wú)論該屬性存在于實(shí)例中還是原型中。

同時(shí)使用hasOwnProperty() 方法和in 操作符,就可以確定該屬性到底是存在于對(duì)象中,還是存在于原型中。

要取得對(duì)象上所有可枚舉的實(shí)例屬性,可以使用ECMAScript 5 的Object.keys() 方法。這個(gè)方法接收一個(gè)對(duì)象作為參數(shù),返回一個(gè)包含所有可枚舉屬性的字符串?dāng)?shù)組。

如果想得到所有實(shí)例屬性,無(wú)論是否可枚舉,可以使用Object.getOwnPropertyNames() 方法。

為了減少不必要的輸入,也為了從視覺(jué)上更好地封裝原型的功能,更常見(jiàn)的做法是用一個(gè)包含所有屬性和方法的對(duì)象字面量來(lái)重寫(xiě)整個(gè)原型對(duì)象。但需注意constructor。

由于在原型中查找值的過(guò)程是一次搜索的,因此我們對(duì)原型對(duì)象所做的任何修改都能夠立即從實(shí)例上反映出來(lái)——即使是先創(chuàng)建了實(shí)例后修改原型也照樣如此。

原型模式缺點(diǎn):

  • 所有實(shí)例在默認(rèn)情況下都將取得相同的屬性值
  • 若原型中包含引用類型值的屬性,會(huì)出現(xiàn)所有實(shí)例共享的問(wèn)題

組合使用構(gòu)造函數(shù)模式和原型模式

創(chuàng)建自定義類型的最常見(jiàn)方式,就是組合使用構(gòu)造函數(shù)模式與原型模式。構(gòu)造函數(shù)模式用于定義實(shí)例屬性,而原型模式用于定義方法和共享的屬性。結(jié)果,每個(gè)實(shí)例都會(huì)有自己的一份實(shí)例屬性的副本,但同時(shí)又共享著對(duì)方法的引用,最大限度地節(jié)省了內(nèi)存。另外,這種混成模式還支持向構(gòu)造函數(shù)傳遞參數(shù);可謂是集兩種模式之長(zhǎng)。示例:

function Person(name, age, job){
  this.name = name;
  this.age = age;
  this.job = job;
  this.friends = ["Shelby", "Court"];
}
Person.prototype = {
  constructor : Person,
  sayName : function(){
    alert(this.name);
  }
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,court,Van"
alert(person2.friends); //"Shelby,court"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

這種構(gòu)造函數(shù)與原型混成的模式,是目前在ECMAScript 中使用最廣泛、認(rèn)同度最高的一種創(chuàng)建自定義類型的方法??梢哉f(shuō),這是用來(lái)定義引用類型的一種默認(rèn)模式。

動(dòng)態(tài)原型模式

function Person(name, age, job){
  //屬性
  this.name = name;
  this.age = age;
  this.job = job;
  //方法
  if (typeof this.sayName != "function"){
    Person.prototype.sayName = function(){
      alert(this.name);
    };
  }
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

繼承

許多OO 語(yǔ)言都支持兩種繼承方式:接口繼承和實(shí)現(xiàn)繼承。接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)現(xiàn)的方法。由于函數(shù)沒(méi)有簽名,在ECMAScript 中無(wú)法實(shí)現(xiàn)接口繼承。ECMAScript 只支持實(shí)現(xiàn)繼承,而且其實(shí)現(xiàn)繼承主要是依靠原型鏈來(lái)實(shí)現(xiàn)的。

原型鏈

構(gòu)造函數(shù)、原型和實(shí)例的關(guān)系:每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針,而實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針。

原型鏈的基本概念為:讓原型對(duì)象等于另一個(gè)類型的實(shí)例。此時(shí)的原型對(duì)象將包含一個(gè)指向另一個(gè)原型的指針,相應(yīng)地,另一個(gè)原型中也包含著一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針。

實(shí)現(xiàn)原型鏈有一種基本模式,代碼大致如下:

function SuperType(){
  this.property = true;
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
};
function SubType(){
  this.subproperty = false;
}
// 通過(guò)重寫(xiě)原型對(duì)象,繼承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
  return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue());  //true

實(shí)現(xiàn)繼承的本質(zhì)是重寫(xiě)原型對(duì)象,代之以一個(gè)新類型的實(shí)例。換句話說(shuō),原來(lái)存在于SuperType 的實(shí)例中的所有屬性和方法,現(xiàn)在也存在于SubType.prototype 中了。新原型不僅具有作為一個(gè)SuperType 的實(shí)例所擁有的全部屬性和方法,而且其內(nèi)部還有一個(gè)指針,指向了SuperType 的原型。

最終的結(jié)果是這樣的:instance 指向了SubType 的原型,SubType 的原型又指向了SuperType 的原型。

需注意instance.constructor 現(xiàn)在指向的是SuperType。

確定原型和實(shí)例的關(guān)系:

  • instanceof
  • isPrototypeOf()

給原型添加方法的代碼一定要放在替換原型的語(yǔ)句之后。

在通過(guò)原型鏈實(shí)現(xiàn)繼承時(shí),不能使用對(duì)象字面量創(chuàng)建原型方法。因?yàn)檫@樣做會(huì)重寫(xiě)原型鏈。

原型鏈的問(wèn)題:

  • 包含引用類型值時(shí)
  • 創(chuàng)建子類型的實(shí)例時(shí),不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)

借用構(gòu)造函數(shù)

在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。函數(shù)只不過(guò)是在特定環(huán)境中執(zhí)行代碼的對(duì)象,因此通過(guò)使用apply() 和call() 方法也可以在(將來(lái))新創(chuàng)建的對(duì)象上執(zhí)行構(gòu)造函數(shù),示例:

function SuperType(){
  this.colors = ["red","blue","green"];
}
function SubType(){
  //繼承了SuperType
  SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);  //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors);  //"red,blue,green"

相對(duì)于原型鏈而言,借用構(gòu)造函數(shù)有一個(gè)很大的優(yōu)勢(shì),即可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)。

缺點(diǎn):方法都需在構(gòu)造函數(shù)中定義,不能復(fù)用函數(shù)。

組合繼承

使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,而通過(guò)借用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。既通過(guò)在原型上定義方法實(shí)現(xiàn)了函數(shù)復(fù)用,又能夠保證每個(gè)實(shí)例都有它自己的屬性。

組合繼承避免了原型鏈和借用構(gòu)造函數(shù)的缺陷,融合了它們的優(yōu)點(diǎn),成為JavaScript 中最常用的繼承模式。

寄生式繼承

function createAnother(original){
  var clone = Object(original); //通過(guò)調(diào)用函數(shù)創(chuàng)建一個(gè)新對(duì)象
  clone.sayHi = function(){ //以某種方式來(lái)增強(qiáng)這個(gè)對(duì)象
    alert("hi");
  };
  return clone; //返回這個(gè)對(duì)象
}

寄生組合式繼承

組合繼承無(wú)論什么情況下,都會(huì)調(diào)用兩次超類型構(gòu)造函數(shù):一次是在創(chuàng)建子類型原型的時(shí)候,另一次是在子類型構(gòu)造函數(shù)構(gòu)造函數(shù)內(nèi)部。

寄生組合式繼承:不必為了指定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),我們所需要的無(wú)非就是超類型原型的一個(gè)副本而已。

function inheritPrototype(subType, superType){
  var prototype = Object(superType.prototype);  //創(chuàng)建對(duì)象
  prototype.constructor = subType;  //彌補(bǔ)下一步重寫(xiě)原型而失去的默認(rèn)的constructor
  subType.prototype = prototype;  //指定對(duì)象
}

function SuperType(name){
  this.name = name;
  this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
  alert(this.name);
};
function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
}
inheriPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
  alert(this.age);
}

這個(gè)例子的高效率體現(xiàn)在它只調(diào)用了一次SuperType 構(gòu)造函數(shù),并且因此避免了在SubType.prototype 上面創(chuàng)建不必要的、多余的屬性。與此同時(shí),原型鏈還能保持不變;因此,還能夠正常使用instanceof 和isPrototypeOf() 。開(kāi)發(fā)人員普遍認(rèn)為寄生組合式繼承是引用類型最理想的繼承范式。

第7章 函數(shù)表達(dá)式

函數(shù)聲明:

function functionName(arg0,arg1,arg2){
  //函數(shù)體
}

函數(shù)表達(dá)式最常見(jiàn)的一種形式:

var functionName = function(arg0,arg1,arg2){
  //函數(shù)體
}

創(chuàng)建一個(gè)函數(shù)并將它賦值給變量,這種情況下創(chuàng)建的函數(shù)叫做匿名函數(shù),又稱拉姆達(dá)函數(shù)。

在把函數(shù)當(dāng)成值來(lái)使用的情況下,都可以使用匿名函數(shù)。

遞歸

遞歸函數(shù)是在一個(gè)函數(shù)通過(guò)名字調(diào)用自身的情況下構(gòu)成的。

var factorial = (function f(num){
  if (num <= 1){
    return 1;
  } else {
    return num * f(num-1)
  }
})

以上代碼創(chuàng)建了一個(gè)名為f() 的命名函數(shù)表達(dá)式,然后將它賦值給變量factorial 。即便把函數(shù)賦值給另一個(gè)變量,函數(shù)的名字f 仍然有效,所以遞歸調(diào)用照樣能正確完成。

閉包

閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見(jiàn)方式就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)。

閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值。閉包所保存的是整個(gè)變量對(duì)象,而不是某個(gè)特殊的變量。

模仿塊級(jí)作用域

JavaScript 沒(méi)有塊級(jí)作用域的概念。在塊語(yǔ)句中定義的變量,實(shí)際上是在包含函數(shù)中而非語(yǔ)句中創(chuàng)建的。

用作塊級(jí)作用域(私有作用域)的匿名函數(shù)的語(yǔ)法如下:

(function(){
  //這里是塊級(jí)作用域
})();

以上代碼定義并立即調(diào)用了一個(gè)匿名函數(shù)。將函數(shù)聲明包含在一對(duì)圓括號(hào)中,表示它實(shí)際上是一個(gè)函數(shù)表達(dá)式。而緊隨其后的另一對(duì)圓括號(hào)會(huì)立即調(diào)用這個(gè)函數(shù)。

無(wú)論在什么地方,只要臨時(shí)需要一些變量,就可以使用私有作用域。

私有變量

任何在函數(shù)中定義的變量,都可以認(rèn)為是私有變量,因?yàn)椴荒茉诤瘮?shù)的外部訪問(wèn)這些變量。私有變量包括函數(shù)的參數(shù)、局部變量和在函數(shù)內(nèi)部定義的其他函數(shù)。

用于訪問(wèn)私有變量的公有方法:在函數(shù)內(nèi)部創(chuàng)建一個(gè)閉包,閉包可以通過(guò)自己的作用域鏈訪問(wèn)函數(shù)內(nèi)部的變量。

有權(quán)訪問(wèn)私有變量和私有函數(shù)的公有方法稱為特權(quán)方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容