翻譯:道奇
做者:Dmitri Pavlutin
原文:Inheritance in JavaScript: Understanding the constructor Propertyhtml
JavaScript有一種有趣的繼承機制:prototypal(原型鏈),大部分剛開始JavaScript的開發人員很難理解它,我也是。bash
JavaScript
中的全部類型(除了null
和undefined
值)都有構造函數屬性,它是繼承的一部分。例如:函數
var num = 150;
num.constructor === Number // => true
var obj = {};
obj.constructor === Object // => true
var reg = /\d/g;
reg.constructor === RegExp; // => true
複製代碼
在這篇文章中,咱們將深刻學習對象的構造函數屬性,它做爲類的公共特性,意味着它能夠用於:學習
instanceOf
的另外選擇)prototype
)引用構造函數在JavaScript
中,原始類型指的是數字、布爾、字符串、symbol
(ES6新增類型),null
和undefined
。除了null
和undefined
以外的任何值都有構造函數屬性,該屬性指的是對應的類型函數:ui
Number()
: (1).constructor === NumberBoolean()
: (true).constructor === BooleanString()
: ('hello').constructor === StringSymbol
的Symbol()
: Symbol().constructor === Symbol經過將它與相應的函數進行比較,可將原始類型的構造函數屬性用於肯定它的類型,例如,驗證值是不是數字:this
if (myVariable.constructor === Number) {
// 當myVariable是數字時,代碼才執行
myVariable += 1;
}
複製代碼
注意,這種方法通常不推薦,更推薦typeof
的方式(見1.1),但這種方法在switch
語句中頗有用,能夠減小if/else
的數量:spa
// myVariable = ...
var type;
switch (myVariable.constructor) {
case Number:
type = 'number';
break;
case Boolean:
type = 'boolean';
break;
case String:
type = 'string';
break;
case Symbol:
type = 'symbol';
break;
default:
type = 'unknown';
break;
}
複製代碼
經過new
運算符調用函數時,會建立一個原始值的包裝對象,new String('str'),new Number(15)
和new Boolean(true)
均可以建立一個包裝對象,但Symbol
是不會建立包裝對象的,由於以new Symbol('symbol')
這種方式調用會產生類型異常的錯誤。prototype
包裝對象容許開發人員將自定義屬性和方法綁定到原始值上,由於JavaScript
不容許原始值有本身的屬性。翻譯
在基於構造函數判斷變量的類型時,這些包裝對象的存在可能會對形成理解上的混亂,由於包裝對象具備與原始值相同的構造函數:code
var booleanObject = new Boolean(false);
booleanObject.constructor === Boolean // => true
var booleanPrimitive = false;
booleanPrimitive.constructor === Boolean // => true
複製代碼
prototype
)對象的構造函數原型(prototype
)中的構造函數屬性會自動設置爲構造函數的引用。
function Cat(name) {
this.name = name;
}
Cat.prototype.getName = function() {
return this.name;
}
Cat.prototype.clone = function() {
return new this.constructor(this.name);
}
Cat.prototype.constructor === Cat // => true
複製代碼
由於屬性是繼承自原型(prototype
)的,實例對象也有構造函數。
var catInstance = new Cat('Mew');
catInstance.constructor === Cat // => true
複製代碼
甚至從直接量上建立的對象,也是繼承自Object.prototype
。
var simpleObject = {
weekDay: 'Sunday'
};
simpleObject.prototype === Object // => true
複製代碼
構造函數是原型對象中常規的不可枚舉屬性,當基於它建立新的對象的時候,它不會自動更新。當建立子類時,須要手動設置正確的構造函數。
下面的例子爲超類Cat
建立一個子類Tiger
。注意初始化時Tiger.prototype
仍然指向Cat
構造函數。
function Tiger(name) {
Cat.call(this, name);
}
Tiger.prototype = Object.create(Cat.prototype);
//prototype有個不正確的構造函數
Tiger.prototype.constructor === Cat // => true
Tiger.prototype.constructor === Tiger // => false
複製代碼
如今若是使用Cat.prototype
中定義的clone()
方法克隆一個Tiger
實例,會建立一個錯誤的Cat
實例。
var tigerInstance = new Tiger('RrrMew');
var wrongTigerClone = tigerInstance.clone();
tigerInstance instanceof Tiger // => true
// 注意wrongTigerClone是個不正確的Cat實例
wrongTigerClone instanceof Tiger // => false
wrongTigerClone instanceof Cat // => true
複製代碼
會出錯的緣由是Cat.prototype.clone()
使用 new this.constructor()
建立新的備份,但構造函數始終指向Cat
函數。
爲了解決這個問題,必需使用正確的構造函數:Tiger
,手動更新Tiger.prototype
,這樣clone()
方法也會被修復。
//修改Tiger原型構造函數
Tiger.prototype.constructor = Tiger;
Tiger.prototype.constructor === Tiger // => true
var tigerInstance = new Tiger('RrrMew');
var correctTigerClone = tigerInstance.clone();
//注意correctTigerClone是正確的Tiger實例
correctTigerClone instanceof Tiger // => true
correctTigerClone instanceof Cat // => false
複製代碼
查看此demo以得到完整的示例。
object instanceof Class
用於肯定對象是否和Class
有一樣的prototype
。
這個操做符也會在原型鏈裏進行搜索,這樣作有時候會使得區分子類實例和超類實例變得很困難,例如:
var tigerInstance = new Tiger('RrrMew');
tigerInstance instanceof Cat // => true
tigerInstance instanceof Tiger // => true
複製代碼
就像這個例子中看到的,不太可能準確的確認tigerInstance
是Cat
仍是Tiger
,由於instanceof
在兩種狀況下都返回true
。 這就是構造函數屬性的閃光點,它容許嚴格肯定實例類。
tigerInstance.constructor === Cat // => false
tigerInstance.constructor === Tiger // => true
// 或者使用switch
var type;
switch (tigerInstance.constructor) {
case Cat:
type = 'Cat';
break;
case Tiger:
type = 'Tiger';
break;
default:
type = 'unknown';
}
type // => 'Tiger'
複製代碼
JavaScript
中的函數對象有個屬性名稱,它返回函數名或匿名函數的空字符串。
除了構造函數屬性以外,這對於肯定類名也頗有用,做爲Object.prototype.toString.call(objectInstance)
的另一種選擇。
var reg = /\d+/;
reg.constructor.name // => 'RegExp'
Object.prototype.toString.call(reg) // => '[object RegExp]'
var myCat = new Cat('Sweet');
myCat.constructor.name // => 'Cat'
Object.prototype.toString.call(myCat) // => '[object Object]'
複製代碼
可是name
返回匿名函數的空字符串(可是在ES6
中,名稱能夠推斷出來),這種方法應該謹慎使用。
構造函數屬性是JavaScript
的繼承機制的一小部分。建立類的層級結構時應採起預防措施, 可是,它提供了肯定實例類型的很好的替代方法。
還能夠看一下
Object.prototype.constructor
What’s up with the constructor property in JavaScript?