JavaScript 學習筆記 之 原生函式
原生函式是什麼
JavaScript的原生函式也叫內建函式
例如 String(),Number(),Boolean()等
原生函式可以被當做建構函式來使用
但通過建構函式(如new String("abc"))創建出來的是封裝了基本型別值("abc")的封裝物件
var abc = new String("abc");
console.log(typeof abc); //object 不是string
[[class]]
[[class]]是所有typeof返回值為"object"的物件的一個內部屬性,這個屬性無法被直接訪問
但可以通過Object.prototype.toString(..)來檢視,可以把它看做是一個內部的分類
Object.prototype.toString.call(abc);//[object String]
多數情況下,物件內部的[[class]]屬性和建立改物件的內建原生建構函式相對應
Object.prototype.toString.call([1, 2, 3]); //[object Array]
但並非總是如此
Object.prototype.toString.call(undefined); //[object Undefined]
可以看到undefined不存在Undefined()這樣的原生建構函式,但是[[class]]仍然是"Undefined"(null的[[class]]是"Null")
而其他的基本型別則存在一個被稱為包裝(boxing)的行為
Object.prototype.toString.call("123"); //[object String]
Object.prototype.toString.call(123); //[object Number]
Object.prototype.toString.call(true); //[object Boolean]
上例中基本型別被各自的封裝物件自動包裝,所以它們的內部[[Class]]值分別為"String","Number","Boolean"
封裝物件包裝
由於基本型別沒有.length這樣的屬性和.toString()這樣的方法
需要通過封裝物件才能訪問,此時JavaScript會自動為基本型別值包裝(box或者wrap)一個封裝物件
var a = "abc";
a.length; //3
想要自行封裝基本型別值可以用Object(..)函式(不加new)
拆封
由於封裝物件是一個物件,所以不管基本型別值是什麼,進行判斷的時候返回的都是一個真值
當我們需要得到封裝物件中的基本型別值時,可以用valueOf(..)函式
var a = Object("abc");
console.log(a.valueOf());//abc
在需要用到封裝函式中的基本型別值的時候會發生隱式解封
var a = Object("abc");
var b = a + "";
console.log(b, typeof b); //abc string
原生函式作為建構函式
使用Array(..)構造一個數組和直接宣告一個數組的效果是一樣的,建立的值都是通過封裝物件來包裝
使用Array(..)建構函式時候帶new和不帶new效果是一樣的,不帶new的時候會自動補上
只帶一個數字引數的時候,該引數會被當做預設長度,而不是當做陣列中的一個元素
如果一個數組沒有任何單元,但是length屬性卻顯示有,這樣會出現空單元的情況
var a = new Array(3);
console.log(a); //(3) [empty × 3]
我們將包含至少一個空單元的陣列稱之為稀疏陣列
在某些方法中空單元的行為和undefined類似,但在另一些方法中又完全不同
var a = new Array(3);
var b = [undefined, undefined, undefined];
console.log(a); //(3) [empty × 3]
console.log(b); //(3) [undefined, undefined, undefined]
console.log(
a.join("-"), //--
b.join("-") //--
);
console.log(
a.map(function(value, index) {
return index;
}), //(3) [empty × 3]
b.map(function(value, index) {
return index;
}) //(3) [0, 1, 2]
);
這是因為join方法會先假定陣列不為空,然後通過length來遍歷
而map方法則不會
由於空單元存在的種種問題,因此我們不建議在任何情況下建立和使用空單元
我們可以用另一種方法來更安全建立undefined值的陣列
var a = Array.apply(null, {
length: 3
});
console.log(a); //(3) [undefined, undefined, undefined]
Array.apply把{length:3}作為引數呼叫Array()方法
在傳入引數時假設apply有個for迴圈來遍歷傳入的類陣列物件
從0開始迴圈到length
但是由於傳入的類陣列物件中並沒有[0],[1],[2]等屬性,於是返回的是undefined
因此實際上執行的就變成了Array(undefined,undefined,undefined)
原生函式的原型
原生建構函式也有自己的.prototype物件,如Array.prototype
這些物件包含其子物件所特有的行為特徵
比如String.prototype.indexOf(..)
根據文件約定,這類方法一般簡寫為String#indexOf(..)
而原生的函式的原型同樣也是不錯的預設值
因為類似於Array.prototype已經被建立過一次了
如果將預設值設定為 [] 的話會導致資源的浪費,因為每次使用都需要建立一次
但是如果預設值隨後會改變的話,就不要使用這種方法
因為修改原型會導致很多問題