第五章:引用類型
本章內(nèi)容:
- 使用對象
- 創(chuàng)建并操作數(shù)組
- 理解基本的JavaScript類型
- 使用基本類型和基本包裝類型
引用類型的值是引用類型的一個實例。在ECMAScript中,引用類型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)據(jù)和功能組合在一起。
對象是某個引用類型的實例。新對象是使用new操作符后跟一個構(gòu)造函數(shù)創(chuàng)建的。構(gòu)造函數(shù)本身是一個函數(shù),只不過該函數(shù)是為了創(chuàng)建新對象的目的而定義的。
var person = new Object();
這行代碼創(chuàng)建了Object引用類型的一個新實例,然后把該實例保存在了變量person中。使用的構(gòu)造函數(shù)是Object,它只為新對象創(chuàng)建了屬性與方法。
5.1 Object類型
Object類型是所有引用類型的父類。
創(chuàng)建Object類型有兩種方式:
// 1.使用new操作符后跟Object構(gòu)造函數(shù)
var person = new Object();
person.name = 'zhangzhuo';
person.age = 18;
// 2.使用對象字面量
var person = {
name: 'zhangzhuo',
age: 18
};
訪問對象屬性有兩種方式:
// 用上面的例子
// 第一種`.`
alert(person.name);
// 第二種使用`[]`
alert(person['name']);
使用方括號的優(yōu)點是可以用變量來訪問
var propertyName = 'name';
alert(person[propertyName])
5.2 Array類型
數(shù)組是值得有序集合。每個值可以叫做一個元素。每個元素在數(shù)組中有一個位置,稱之為索引。Javascript數(shù)組是無類型的,即每個元素可以是不同類型。
每個數(shù)組都有一個length屬性,表示當(dāng)前數(shù)組的長度。
數(shù)組繼承來自Array.prototype的屬性,
創(chuàng)建數(shù)組
// 第一種 new
var colors = new Array();
// 第二種 字面量
var colors = ['red','yellow'];
5.2.1 檢測數(shù)組
// 方法一
if(value instanceof Array){
// do something
}
instanceof的問題在網(wǎng)頁中如果包含多個框架的話就會出現(xiàn)問題。
// 方法二
if(Array.isArray(value)){
// do some thing
}
isArray實現(xiàn)代碼如下:
var isArray = Function.isArray || function(o){
return typeof o == 'object' &&
Object.prototype.toString.call(o) === '[object Array]';
}
5.2.3 棧方法
棧是一種LIFO(Last-In-First-Out 后進先出)的數(shù)據(jù)格式,用Array可以模仿棧的操作。
// 數(shù)組模擬棧操作
var colors = new Array();
colors.push('yellow'); // ['yellow']
colors.push('orange'); //['yellow','orange'];
var item = colosr.pop(); // item=>'orange' ['yellow']
函數(shù)名 | 說明 | 返回值 |
---|---|---|
push | 數(shù)組元素的尾部添加一個或多個元素 | 數(shù)組長度 |
pop | 刪除最后一個元素 | 刪除的值 |
5.2.4 隊列方法
隊列是一種FIFO(First-In-First-Out 先進先出)的數(shù)據(jù)格式,隊列在列表末端添加元素,在列表前端刪除項。
可以使用push和shift或者unshift和pop
// 數(shù)組模擬棧操作
var colors = new Array();
colors.push('yellow'); // ['yellow']
colors.push('orange'); //['yellow','orange'];
var item = colors.shift(); // item=>'yellow' ['orange']
函數(shù)名 | 說明 | 返回值 |
---|---|---|
shift | 刪除第一個元素 | 刪除的值 |
unshift | 元素的首端添加一個或多個元素 | 數(shù)組長度 |
5.2.6 操作方法
concat()
Array.concat方法傳遞并返回一個新數(shù)組,它的元素包括調(diào)用concat()的原始數(shù)組和concat的每個參數(shù)。如果參數(shù)中任何一個自身是數(shù)組,則鏈接數(shù)組的元素,并非數(shù)組的本身。但要注意,concat不會遞歸數(shù)組的數(shù)組。
var a = [1,2,3];
a.concat(4,5); // [1,2,3,4,5]
a.concat([4,5]); // [1,2,3,4,5]
a.concat(4,[5,[6,7]]); // [1,2,3,4,5,[6,7]]
slice()
Array.slice()返回制定數(shù)組的一個片段或子數(shù)組,它的兩個參數(shù)分別指定了起始于終止的位置。如果只有一個參數(shù),則從起始位置到數(shù)組末端。
var a = [1,2,3,4,5];
a.slice(0,3); // [1,2,3]
a.slice(1); //[2,3,4,5]
slice方法可以讓類數(shù)組轉(zhuǎn)換為真正數(shù)組 Array.prototype.slice.call(xx);
splice()
Array.splice()方法是在數(shù)組中插入和刪除元素的通用方法,不同slice和concat會返回新數(shù)組。而splice會修改調(diào)用的數(shù)組。
- 刪除: 兩個參數(shù), 要刪除第一項的位置,和刪除的項數(shù)。 eg: slice(0,2)
- 插入: 三個參數(shù),起始位置,0(需要刪除的項數(shù)),要添加的項。如果要插入多個,則可傳遞4,5,6
- 替換: 三個參數(shù),起始位置,刪除的項數(shù),要添加的項。(可以理解為 刪除+插入)
splice始終會返回一個數(shù)組,包含從原數(shù)組刪除的項。
// demo1
var colors = ['red','orange','blue'];
var removed = colors.splice(0,2); //刪除前兩項
alert(removed); //['red','orange']
alert(colors); ['blue']
// demo2
var colors = ['red','orange','blue'];
var removed = colors.splice(0,0,'pink','yellow'); // 首位添加pink
alert(removed); //[]
alert(colors); //['pink','yellow','red','orange','blue'];
// demo3
var colors = ['red','orange','blue'];
var removed = colors.splice(0,1,'pink'); // 更換第一位置的red => pink
alert(removed); //['red']
alert(colors); //['pink''orange','blue'];
5.2.6 迭代方法
ECMAScript 5為數(shù)組定義了5個迭代方法。每個方法都接受兩個參數(shù):要在每一項上運行的函數(shù)和(可選)運行該函數(shù)的作用域?qū)ο?-影響this值。
傳入這些方法中的函數(shù)會接受三個參數(shù):數(shù)組項的值,該項在數(shù)組的位置,數(shù)組本身。
- every(): 如果每一項都返回true, 則返回true;
- some(): 如果有一項返回true,則為true;
- filter(): 返回該函數(shù)會返回true的項所組成的數(shù)組;
- forEach(); 迭代循環(huán)像for,沒有返回值;
- map(): 返回每次調(diào)用的結(jié)果組成的數(shù)組
5.2.6 歸并方法
ECMAScript 5為數(shù)組定義了2個歸并方法reduce和reduceRight。
迭代所有的項,然后構(gòu)建一個最終的返回值。
接受兩個參數(shù),要在每一項執(zhí)行的函數(shù)(可選)作為歸并的初始值。傳入函數(shù)接受四個參數(shù),前一個值,當(dāng)前值,項的索引和數(shù)組本身。
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur,index,array){return prev+cur});
alert(sum); //10
類數(shù)組
能通過Array.prototype.slice轉(zhuǎn)換為真正的數(shù)組的帶有l(wèi)ength屬性的對象。
類數(shù)組擁有以下特征:
- 具有l(wèi)ength屬性
- 可以被遍歷
這種對象有很多,比較特別的是arguments對象,還有像調(diào)用getElementsByTagName,document.childNodes之類的,它們都返回NodeList對象都屬于偽數(shù)組。
我們可以通過Array.prototype.slice.call(fakeArray)將偽數(shù)組轉(zhuǎn)變?yōu)檎嬲腁rray對象。
來看個示例
var fakeArray01 = {0:'a',1:'b',length:2};//這是一個標(biāo)準(zhǔn)的有偽數(shù)組對象
var arr01 = Array.prototype.slice.call(fakeArray01);
alert(arr01[0]);//a
var arr02 = [].slice.call(fakeArray01);
alert(arr02[0]);//a
類數(shù)組的實現(xiàn)
我們來看一些特殊的用例:
var fakeArray01 = {a:'a',b:'b',length:2};//沒有l(wèi)ength下標(biāo)對應(yīng)的值
var arr01 = Array.prototype.slice.call(fakeArray01); // [empty * 2]
alert(arr01[0]);//undefined
var fakeArray02 = {0:'a',1:'b',length:'num'};//length不是數(shù)值
var arr02 = Array.prototype.slice.call(fakeArray02); []
alert(arr02[1]);//undefined
同樣fakeArray01和fakeArray02被轉(zhuǎn)換成了真正的數(shù)組,但是數(shù)組中的值都為undefined
查看 V8 引擎[的源碼,可以將 slice 的內(nèi)部實現(xiàn)簡化為:
function ArraySlice(start, end) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
var len = TO_UINT32(this.length);
var start_i = TO_INTEGER(start);
var end_i = len;
if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);//如果沒傳入end,end=length,即slice第二個參數(shù)可選。
if (start_i < 0) {
start_i += len;//參數(shù)1的A分支 處理負(fù)值,+= length。如:為-1,則start從倒數(shù)第一個開始,負(fù)值絕對值小于length
if (start_i < 0) start_i = 0;//參數(shù)1的A.a分支 若仍未負(fù)值,則等于0。 即處理負(fù)值絕對值大于length [1,2,3].slice(-4)。
} else {
if (start_i > len) start_i = len;//參數(shù)1的B分支 參數(shù)大于length,則等于length,處理 [1,2,3].slice(5),返回[]
}
if (end_i < 0) {
end_i += len;//參數(shù)2的A分支 處理負(fù)值,+= length。如:為-1,則start從倒數(shù)第一個開始,負(fù)值絕對值小于length
if (end_i < 0) end_i = 0;//參數(shù)2的A.a分支 若仍未負(fù)值,則等于0。 即處理負(fù)值絕對值大于length [1,2,3].slice(1,-4)。
} else {
if (end_i > len) end_i = len;//參數(shù)2的B分支 參數(shù)大于length,則等于length,處理 [1,2,3].slice(1,5) == [1,2,3].slice(1) ==
}
//最終返回結(jié)果的值。可以看到這里會返回一個新的真正的數(shù)組(ps:slice的好基友splice是修改原數(shù)組的。)
var result = [];
// 處理分支1 如果經(jīng)歷了上面代碼的層層檢查設(shè)置,結(jié)束值小于開始值,那么直接返回空數(shù)組,處理 [1,2,3].slice(2,1)
if (end_i < start_i) return result;
// 處理分支2 如果是數(shù)組 && !%IsObserved(this) && 結(jié)束大于1000 && %EstimateNumberOfElements(this) < 結(jié)束值 ,那么使用方法SmartSlice來處理
if (IS_ARRAY(this) &&
!%IsObserved(this) &&
(end_i > 1000) &&
(%EstimateNumberOfElements(this) < end_i)) {
SmartSlice(this, start_i, end_i - start_i, len, result);
} else {
// 處理分支2 調(diào)用SimpleSlice 處理。
SimpleSlice(this, start_i, end_i - start_i, len, result);
}
//設(shè)置length,似乎多余?還是v8中的數(shù)組[] 需指定length。 此處待探尋。。。
result.length = end_i - start_i;
return result;
}
/*
* ......
*/
// Set up non-enumerable functions of the Array.prototype object and
// set their names.
// Manipulate the length of some of the functions to meet
// expectations set by ECMA-262 or Mozilla.
InstallFunctions($Array.prototype, DONT_ENUM, $Array(
//......
"slice", getFunction("slice", ArraySlice, 2)
//......
));
可以看出,slice 并不需要 this 為 array 類型,只需要有 length 屬性即可。并且 length 屬性可以不為 number 類型,當(dāng)不能轉(zhuǎn)換為數(shù)值時,ToUnit32(this.length) 返回 0.
根據(jù)以上結(jié)論可以得出:fakeArray01被轉(zhuǎn)換成了lenth為2的數(shù)組,其值都被初始化為undefined,fakeArray02被轉(zhuǎn)換成了length為0的數(shù)組,自然訪問下標(biāo)為1的元素返回undefined
為什么使用類數(shù)組?
因為純數(shù)組不能增加額外的屬性。 arguments對象還有個callee屬性。
5.4 RegExp 類型
ECMAScript通過RegExp類型來支持正則表達式。可以使用以下兩種方式創(chuàng)建正則表達式:
var pattern1 = new RegExp(); // 用構(gòu)造函數(shù)
var expression = / pattern / flags; // 使用字面量
其中的模式(pattern)部分可以是簡單或復(fù)雜的正則表達式,可以包含字符類、限定類、分組、向前查找以及反向引用。每個表達式可以帶一個或多個標(biāo)志(flags),用于表達正則表達式的行為。正則表達式的匹配模式支持下列3個標(biāo)志(也可稱為修飾符
):
- g: 表示全局模式(global)模式,即模式應(yīng)用于所有的字符串;
- i: 表示不區(qū)分大小寫(case-insensitive)模式, 即在確定匹配時忽略字符串的大小寫;
- m: 表示多行模式,即在達到一行末尾時還會匹配下一行;
字符類
將直接量字符單獨放進方括號內(nèi)就組成了字符類(character class)。 一個字符類可以匹配它所包含的任意字符。因此/[abc]/
就和'a'、'b'、'c'任意一個匹配。
/[abc]/.test('a'); // true
/[abc]/.test('123'); // false
另外可以通過 ^
符號作為否定字符類,將^
作為左方括號的第一個字符。
/[^abc]/.test('a'); // false
字符 | 匹配 |
---|---|
[...] | 方括號任意字符 |
[^...] | 不在方括號任意字符 |
. | 除了換行符和Unicode行終止符以外的字符 |
\w | 任何ASCII字符組成的單詞,等價于[a-zA-Z0-9] |
\W | 任何不是ASCII字符組成的單詞, 等價于上面的非 |
\s | 任何Unicode空白符 |
\S | 任何非Unicode空白符的字符 |
\d | 任意ASCII數(shù)字,等價于[0-9] |
\D | 除了ASCII數(shù)字以外的字符 |
pattern = /at/g; //匹配'at'
pattern = /[bc]at/i; // 匹配第一個'bat'或'cat'
重復(fù)類
定義指定標(biāo)識能重復(fù)出現(xiàn)的標(biāo)志。
字符 | 含義 |
---|---|
{n,m} | 匹配前一項至少n次,至多m次 |
{n,} | 匹配前一項至少n次或者更多 |
{n} | 匹配前一項n次 |
? | 匹配前一項0次或者1次,也就說是可選的,等價于{0,1} |
+ | 匹配前一項1次或者多次,等價于{1,} |
* | 匹配前一項0次或多次, 等價于{0,} |
pattern = /\d{2,4}/; //匹配2-4個數(shù)字
pattern = /\w{3}/; //匹配3個字母
pattern = /\s+java\s+/; //匹配前后帶有一個或多個的空格符的字符串java
pattern = /[^(]*/; //匹配多個非括號的字符
選擇、分組和引用
字符 | 含義 |
---|---|
| | 選擇該字符左邊的表達式或者右邊的表達式(有短路特性) |
(...) | 組合, 將幾個項組合為一個單元,這個單元可供‘*’、‘+’、'?'、‘|’等符號加以修飾,而且可以記住這個組合相匹配的字符串以供此后引用使用 |
(?:...) | 只組合,把項組合到一個單元,但不記憶與該組合匹配的字符串 |
\n | 和第n個分組第一次匹配的字符相匹配,組是圓括號的子表達式(也有可能是嵌套的),組索引是從左向右的左括號數(shù),(?::形式不編碼 |
指定匹配位置
將模式定位在搜索字符串的特定位置上
字符 | 含義 |
---|---|
^ | 匹配字符串開頭,在多行檢索中,匹配一行的開頭 |
$ | 匹配字符串的結(jié)尾,在多行檢索中,匹配一行的結(jié)尾 |
pattern = /^Javascript$/; //匹配單詞'Javascript'
用于模式匹配的string方法
string 支持四種正則表達式的方法:
search(pattern)
'Javascript'.search(/java/i); // 0
replace(pattern,repleaceStr)
進行檢索與替換。參數(shù)第一個為正則表達式,第二個為要替換的字符串。
// 將不區(qū)別大小寫的javascript替換為正確的JavaScript
'javascript&Javascript'.replace(/javascript/gi,'JavaScript'); //JavaScript&JavaScript
/*
* replace功能還不止這樣。
* 正則表達式圓括號起來的子表達式是帶有從左向右的索引編號的,而且表達式會記憶與每個子表達式匹配的文本。
* 如果替換文字是$加數(shù)字,那么replace將用于指定的子表達式相匹配的字符串來替換著兩個字符
*/
// demo: 將英文引號替換中文引號
'"hello wolrd"'.replace(/"([^"]*)"/g,' “$1” '); // “ hello wolrd ”
mattch(pattern)
唯一的參數(shù)是正則表達式,返回的是由匹配結(jié)果組成的數(shù)組
"1 plus 2 equals 3".match(/\d+/g); // ["1", "2", "3"]
split(pattern|str)
拆分方法,將字符串以一種分隔符拆分為數(shù)組, 參數(shù)是分隔符或者正則,返回拆分后的數(shù)組
"1,2,3,".split(','); //['1','2','3']
"1, 2, 3".split(/\s*,\s*/); //['1','2','3']
RegExp實例方法
exec(str)
RegExp對象的主要方法是exec()
,該方法主要用于捕獲組而設(shè)計的。exec()接受一個參數(shù),即應(yīng)用模式的字符串組。然后返回包含第一個匹配信息的數(shù)組。如果沒有匹配則返回null。返回的數(shù)組雖然是Array的實例,但包含了兩個額外的屬性:index,和input。其中index表示匹配項在字符串中的位置。input表示應(yīng)用正則表達式的字符串。在數(shù)組中,第一項是整個模式匹配的字符串,其他項是與模式中捕獲組匹配的字符串(如果沒有捕獲組,則該數(shù)組只有一項)
var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
alert(matches.index); //0
alert(matches.input); //mom and dad and baby
alert(matches[0]); //mom and dad and baby
alert(matches[1]); // and dad and baby
alert(matches[2]); // and baby
test(str)
如果只想知道是否匹配,可以用test。接受一個字符串參數(shù),匹配返回true。
var text = '000-00-0000';
var pattern = /\d{3}-\d{2}-\d{4}/ig;
if(pattern.test(text)){
alert("this pattern was matched")
}
5.5 Function類型
函數(shù)實際上是對象。每個函數(shù)都是Function的一個實例,因此函數(shù)名是指向函數(shù)對象的指針,不會與某個函數(shù)綁定。
// 函數(shù)聲明語法
function sum(num1, num2){
return num1 + num2;
}
// 函數(shù)表達式
var sum = function(num1, num2){
return num1 + num2;
}
這兩種方式其實沒有太大區(qū)別,只是在環(huán)境變量創(chuàng)建時候,函數(shù)聲明語法會被解析器早一些被讀取。
由于一個函數(shù)名僅僅是指向函數(shù)的指針,因此函數(shù)名與包含對象的指針沒有什么區(qū)別。一個函數(shù)可以有多個函數(shù)名。
function sum(num1, num2){
return num1 + num2;
}
alert(sum(1,2)); //3
var anotherSum = sum;
sum = null;
alert(anotherSum(2,2)); //4
5.5.1 沒有重載(深入理解)
先看一個例子:
function addSomeNumber(num){
return num + 10;
}
function addSomeNumber(num){
return num + 20;
}
alert(addSomeNumber(10)); //30
其實代碼與下面的相同
var addSomeNumber = function(num){
return num + 10;
}
addSomeNumber = function addSomeNumber(num){
return num + 20;
}
alert(addSomeNumber(10)); //30
創(chuàng)建第二個函數(shù)的時候,實際上覆蓋了引用第一個函數(shù)的變量addSomeNumber;即無法實現(xiàn)重載
5.5.2 函數(shù)聲明與函數(shù)與表達式
解析器在執(zhí)行環(huán)境中加載數(shù)據(jù)的時候(創(chuàng)建階段),對函數(shù)聲明與函數(shù)表達式并非一視同仁。解析器會率先讀取函數(shù)聲明,使其在執(zhí)行任何代碼之前可以使用,函數(shù)表達式要在解析器執(zhí)行到它所在的代碼行,才會真正的被解析執(zhí)行。(關(guān)于執(zhí)行具體順序可以看我第四章中的變量對象創(chuàng)建過程
)
alert(sum(10,10)) // 20
alert(otherSum(20,20)); // error otherSum is not a function
function sum(num1,num2){
return num1 + num2;
}
var otherSum = function(num1,num2){
return num1 + num2;
}
在變量對象創(chuàng)建過程函數(shù)表達式被提前,變量提升,實際解析器等價于這樣。
function sum(num1, num2){
return num1 + num2;
}
var otherSum;
alert(sum(10,10)) // 20
alert(otherSum(20,20)); // error otherSum is not a function
otherSum = function(num1,num2){
return num1 + num2;
}
作為值的函數(shù)
在ECMASscript中函數(shù)名本身就是變量,所以函數(shù)可以作為值來使用。也就是說,可以向傳遞參數(shù)一樣把一個函數(shù)傳遞到另一個函數(shù),還可以在將函數(shù)作為另一個函數(shù)的結(jié)果返回。
將函數(shù)作為參數(shù)傳遞
function callOhterFunction(someFunction, someArguments){
return someFunction(someArguments);
}
function add10(num){
return num + 10
}
var result1 = callOhterFunction(add10, 10);
alert(result1); //20
function getGretting(name){
return 'hello '+name;
}
var result2 = callOhterFunction(getGretting, 'zz');
alert(result2); //hello zz
將函數(shù)中在返回另一個函數(shù)
function createComparisonFunction(propertypeName){
return function(object1,object2){
var value1 = object1[propertypeName];
var value2 = object2[propertypeName];
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
5.5.3 函數(shù)內(nèi)部屬性
在函數(shù)內(nèi)部有兩個特殊的對象: arguments
、this
和es5中的caller
arguments
arguments是個類數(shù)組對象,包含了傳遞的所有參數(shù)。雖然arguments的主要用途是保存參數(shù),但這個對象還有一個名為callee的屬性,該屬性是一個指針用來指向擁有arguments對象的函數(shù)(正在執(zhí)行的函數(shù))。
// 經(jīng)典的階乘函數(shù)
function factorial(num){
if(num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
factorial(5); //120
這會產(chǎn)生一個問題:函數(shù)體內(nèi)部與函數(shù)名高度耦合。
var otherFactorial = factorial;
factorial = null;
otherFactorial(5); // error: factorial is not a function
這里使用callee屬性改寫(這樣就可以解除耦合關(guān)系)
// 使用callee改寫
function factorial(num){
if(num <= 1){
return 1;
} else {
return num * arguments.callee(num -1);
}
}
factorial(5); //120
this
可以理解this引用的函數(shù)執(zhí)行的環(huán)境對象。(后面第七章會詳細(xì)講解this)
window.color = 'red';
var o = {color:'blue'};
function sayColor(){
alert(this.color);
}
sayColor(); //red
o.sayColor = sayColor;
o.sayColor(); ;//blue
請記住:函數(shù)的名字僅僅是一個包含指針的變量而已。因此,即使在不同環(huán)境中執(zhí)行,全局的sayColor()函數(shù)與o.sayColor()指向的仍是同一個函數(shù)。
caller
這個屬性保存著調(diào)用這個當(dāng)前函數(shù)的函數(shù)的引用。如果在全局作用域下調(diào)用當(dāng)前函數(shù),它的值為null
function outer(){
inner();
}
function inner(){
alert(inner.caller);
}
outer(); // 打印出outer的源代碼
inner(); // null (全局函數(shù)中調(diào)用,返回null)
關(guān)于callee和caller
在嚴(yán)格模式下,為了加強安全性,這兩個屬性的讀寫操作都會產(chǎn)生類型錯誤,不然第三方的代碼就能相同的環(huán)境中窺視其他的代碼。
arguments.callee 與 caller(function)。
5.5.5 函數(shù)的屬性和方法
每個函數(shù)都包含的屬性:length
和 prototype
。
length
代表希望接收的命名參數(shù)的個數(shù)。(注意:arguments.length指的是實際接收的參數(shù)個數(shù))
function sayName(name){
alert(name);
}
function sum(num1, num2){
return num1 + num2;
}
function sayHi(){
alert('hello')
}
alert(sayName.length); //1
alert(sum.length); //2
alert(sayHi.length); //0
prototype
:指向一個對象的引用,這個對象稱為原型對象
,繼承公共方法,都是依靠此屬性(在第六章會詳細(xì)講解)。
每個函數(shù)都包含了兩個非繼承的方法:apply()
和call()
。
這兩個方法的用途都是在特定作用域下調(diào)用函數(shù),實際上是設(shè)置了函數(shù)體內(nèi)this對象的值。
apply()方法接受兩個參數(shù), 一個是運行函數(shù)的作用域,另一個是參數(shù)數(shù)組,也可以是Array的實例,也可以是arguments對象。
call()方法接受兩個參數(shù),第一個是運行函數(shù)作用域,第二個是參數(shù)需要逐個列舉出來。
function sum(num1,num2){
return num1 + num2;
}
function applySum1(num1, num2){
return sum.apply(this, arguments); //傳遞arguments對象
}
function applySum2(num1, num2){
return sum.apply(this, [num1, num2]); //傳遞數(shù)組
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
applySum1(10,10); //20
applySum2(10,10); //20
callSum(10,10); //20
實際上apply和call真正的勇武之地:他們真正強大的地方是可以擴充函數(shù)賴以生存的作用域。
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
使用call(apply)來擴充作用域的最大好處,就是對象不需要與方法有任何耦合關(guān)系。在前面一個例子第一個版本,我們先將函數(shù)放到對象o中o.sayColor = sayColor
然后再通過o來調(diào)用它,而在通過call后就不需要那些重復(fù)的步驟了。
es5還新增了一種方法:bind()。這個方法會創(chuàng)建一個函數(shù)的實例,其this會被綁定到傳給bind()函數(shù)的值。
window.color = 'red';
var o = {color:'blue'};
function sayColor(){
alert(this.color);
}
var otherSayColor = sayColor.bind(o);
otherSayColor(); //blue
5.6 基本包裝類型
為了方便操作基本類型值,ECMAScript提供了3中特殊的引用類型:Boolean、Number、String。
每當(dāng)讀取一個基本類型值的時候,后臺就會創(chuàng)建一個對應(yīng)的基本包裝類型的對象。
var s1 = "some text";
var s2 = s1.substring(2);
alert(s2); // "me text"
基本類型不是對象,它應(yīng)該不能有方法。其實,為了讓我們實現(xiàn)這種直觀的操作方式,后臺已經(jīng)自動完成了一系列處理。
當(dāng)?shù)诙凶x取s1時,訪問過程處于一種讀取模式,也就是要從內(nèi)存中讀取s1的值。而在讀取模式中訪問字符串,后臺會自動完成下列操作:
- 創(chuàng)建String的一個實例。
- 在實例上調(diào)用指定方法。
- 銷毀這個實例。
可以將上面步驟想象如下代碼:
var s1 = new String('some text');
var s2 = s1.substring(2);
s1 = null;
引用類型與基本包裝類型的主要區(qū)別在于對象的生存期。
使用new操作符創(chuàng)建引用類型的實例,在執(zhí)行流離開當(dāng)前作用域之前一直保存在內(nèi)存中。而自動創(chuàng)建的自動包裝類型,則只存在于一行代碼的執(zhí)行瞬間,然后立馬銷毀。所以我們無法再運行時為基本類型值添加屬性與方法。
var s1 = 'some text';
s1.color = 'red';
alert(s1.color); //undefined
原因就是第二行創(chuàng)建String對象在第三行代碼時已經(jīng)銷毀了。第三行代碼又創(chuàng)建了一個自己的String對象,然而這個對象沒有color屬性。
5.7 單體對象
5.7.1 Global對象
URL編碼方法:
Global對象的encodeURI()和encodeURIComponent()方法對URI進行編碼,以便發(fā)送給瀏覽器。
encodeURI主要對整個URI, 而encodeURIComponent主要對URI的某一段進行編碼。
主要區(qū)別有:encodeURI不會對本身屬于URI的特殊字符做編碼,例如冒號,正斜杠,問好,井號。 而enocdeURIComponent()則會對任何非標(biāo)準(zhǔn)字符串做編碼。
var uri = "http://www.wrox.com/illegal value.html#start";
alert(encodeURI(uri)); // http://www.wrox.com/illegal%20value.html#start
alert(encodeURIComponent(uri)); // http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.html%23start
一般來說,我們使用encodeURIComponent要比encodeURI要多,因為實踐中更常見的是對查詢字符串參數(shù)而不是對基礎(chǔ)uri進行編碼
小結(jié):
對象在javascript中被稱為引用類型的值,而且有一些引用類型可以用來創(chuàng)建特定的對象。總結(jié)如下:
- 引用類型與 傳統(tǒng)的面向?qū)ο蟪绦蛑械念愊嘧R,但實現(xiàn)不同;
- Object類型是一個基礎(chǔ)類型,其他所有類型都繼承Object的基本行為;
- Array類型是一組值得有序列表,同時還提供了操作和轉(zhuǎn)換這些值得方法;
- Date類型提供了日期和時間的信息,還有包括時間的計算方法;
- RegExp類型是ECMASscript支持正則表達式的一個接口,提供了基本的和高級的正則表達式功能;
函數(shù)實際上是Function的實例,因此函數(shù)也是對象;由于函數(shù)是對象,函數(shù)也擁有方法,可以增強其行為。
因為有基本包裝類型,所以Javascrip中的基本類型值可以當(dāng)做對象來訪問。有三種基本包裝類型:Boolean、Number、String。以下是他們的共同特征:
- 每個包裝類型都映射到同名的基本類型;
- 在讀取模式下訪問基本類型的值,就會自動創(chuàng)建對應(yīng)的基本包裝類型的一個對象,從而方便了數(shù)據(jù)操作;
- 基本包裝類型的語句一經(jīng)執(zhí)行完畢,就會立馬銷毀新創(chuàng)建的包裝對象;
在左右代碼執(zhí)行之前,作用域就存在兩個內(nèi)置對象:Global和Math。在大多數(shù)ECMAScript實現(xiàn)中都不能直接訪問Global對象;不過Web瀏覽器實現(xiàn)承擔(dān)該角色是window
對象。全局變量和函數(shù)都是Global對象的屬性。Math提供了很多關(guān)于計算的屬性和方法。