1. 程式人生 > >Effective JavaScript Item 40 避免繼承標準類型

Effective JavaScript Item 40 避免繼承標準類型

nds rac data csdn fin item 文件夾 字符串 html

本系列作為Effective JavaScript的讀書筆記。

ECMAScript標準庫不大。可是提供了一些重要的類型如ArrayFunctionDate。在一些場合下。你或許會考慮繼承當中的某個類型來實現特定的功能。可是這樣的做法並不被鼓舞。

比方為了操作一個文件夾。能夠讓文件夾類型繼承Array類型例如以下:


function Dir(path, entries) {
	this.path = path;
	for (var i = 0, n = entries.length; i < n; i++) {
		this[i] = entries[i];
	}
}
Dir.prototype = Object.create(Array.prototype);
// extends Array

var dir = new Dir("/tmp/mysite", ["index.html", "script.js", "style.css"]);
dir.length; // 0

可是能夠發現。dir.length的值是0,而不是期待中的3

發生這樣的現象的原因在於:僅僅有當對象是真正的Array類型時,length屬性才會起作用。

ECMAScript標準中。定義了一個不可見的內部屬性被稱為 [[class]]。該屬性的值僅僅是一個字符串,所以不要被誤導覺得JavaScript也實現了自己的類型系統。所以,對於Array類型,這個屬性的值就是“Array”。對於Function類型。這個屬性的值就是“Function”。下表是ECMAScript定義的全部[[class]] 值:


技術分享


那麽當對象的類型確實是Array時,length屬性的特別之處就在於:

length的值會和該對象中被索引的屬性個數保持一致。比方對於一個數組對象arrarr[0]arr[1]就表示該對象有兩個被索引的屬性。那麽length的值就是2。當加入了arr[2]的時候。length的值會被自己主動同步成3

相同地,當設置length值為2時。arr[2]會被自己主動設置成undefined

可是當繼承Array類型並創建實例時,該實例的 [[class]] 屬性並非Array。而是Object。因此length屬性不能正確的工作。

JavaScript中,也提供了用於查詢 [[class]] 屬性的方法,即使用Object.prototype.toString

方法:


var dir = new Dir("/", []);
Object.prototype.toString.call(dir); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"

因此,更好的實現方法是使用組合而不是繼承:


function Dir(path, entries) {
	this.path = path;
	this.entries = entries; // array property
}
Dir.prototype.forEach = function(f, thisArg) {
	if (typeof thisArg === "undefined") {
		thisArg = this;
	}
	this.entries.forEach(f, thisArg);
};

以上代碼將不再使用繼承。而是將一部分功能代理給內部的entries屬性來實現。該屬性的值是一個Array類型對象。

ECMAScript標準庫中,大部分的構造函數都會依賴內部屬性值如 [[class]] 來實現正確的行為。對於繼承這些標準類型的子類型。無法保證它們的行為是正確的。

因此。不要繼承ECMAScript標準庫中的類型如:

Array Boolean Date Function NumberRegExpString

總結

  1. 繼承標準類型可能會導致子類的行為不對,由於標準類型會依賴於內部屬性諸如 [[class]]
  2. 優先使用組合的方式來實現功能。而不是使用繼承。




Effective JavaScript Item 40 避免繼承標準類型