原生函數是什麼
JavaScript的原生函數也叫內建函數
例如 String(),Number(),Boolean()等
原生函數可以被當做構造函數來使用
但通過構造函數(如new String("abc"))創建出來的是封裝了基本類型值("abc")的封裝對象
var abc = new String("abc");
console.log(typeof abc); //object 不是string
[[class]]
[[class]]是所有typeof返回值爲"object"的對象的一個內部屬性(函數的typeof返回值爲"function"),這個屬性無法被直接訪問
但可以通過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已經被創建過一次了
如果將默認值設定爲 [] 的話會導致資源的浪費,因爲每次使用都需要創建一次
但是如果默認值隨後會改變的話,就不要使用這種方法
因爲修改原型會導致很多問題