翻译:道奇
做者: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?