1. 程式人生 > >JavaScript 學習筆記 之 原生函式

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已經被建立過一次了

如果將預設值設定為 [] 的話會導致資源的浪費,因為每次使用都需要建立一次

但是如果預設值隨後會改變的話,就不要使用這種方法

因為修改原型會導致很多問題