Effective JavaScript Item 40 避免繼承標準類型
本系列作為Effective JavaScript的讀書筆記。
ECMAScript標準庫不大。可是提供了一些重要的類型如Array,Function和Date。在一些場合下。你或許會考慮繼承當中的某個類型來實現特定的功能。可是這樣的做法並不被鼓舞。
比方為了操作一個文件夾。能夠讓文件夾類型繼承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值為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。 Number,RegExp,String
總結
- 繼承標準類型可能會導致子類的行為不對,由於標準類型會依賴於內部屬性諸如 [[class]]
- 優先使用組合的方式來實現功能。而不是使用繼承。
Effective JavaScript Item 40 避免繼承標準類型