instancof运算符
instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例。
function Vehicle() { }
var v = new Vehicle()
console.log(v instanceof Vehicle) // true
上面代码中,对象v是构造函数Vehicle的实例,所以返回true。
instancof运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象(prototype),是否在左边对象的原型链上。隐藏,下面两种写法是等价的。
v instanceof Vehicle
// 等同于
Vehicle.prototype.isPrototypeOf(v)
由于instanceof检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回true。
var d = new Date()
console.log(d instanceof Date) // true
console.log(d instanceof Object) // true
上面代码中,d同时是Date和Object的实例,因此对这两个构造函数都返回true。
由于任意对象(出了null)都是Object的实例,所以instanceof运算符可以判断一个值是否为非null的对象。
var obj = {
foo:123
}
console.log(obj instanceof Object) // true
console.log(null instanceof Object) // false
上面代码中,除了null,其他对象的instancof Object的运算结果都是true。
instanceof的原理是检查右边构造函数的prototype属性,是否在左边对象的原型链上。有一种特殊情况,就是左边对象的原型链上,只有null对象。这时,instanceof判断会失真。
var obj = Object.create(null)
console.log(typeof obj) // object
console.log(obj instanceof Object) // false
上面代码中,Object.create(null)返回一个新对象obj,它的原型是null。右边的构造函数Object的prototype属性,不在左边的原型链上,因此instanceof就认为obj不是Object的实例。但是,只要一个对象的原型不是null,instanceof运算符的判断就不会失真。
var x = [1,2,3]
var y = {}
console.log(x instanceof Array) // true
console.log(x instanceof Object) // true
console.log(y instanceof Object) // true
上面代码中,instanceof运算符判断,变量x是数组,变量y是对象。
注意,instanceof运算符只能用于对象,不适用原始类型的值。
var s = 'hello'
console.log(s instanceof String) // false
上面代码中,字符串不是String对象的实例(因为字符串不是对象),所以返回false。
此外,对于undefined和null,instanceof运算符总是返回false。
console.log(undefined instanceof Object) // false
console.log(null instanceof Object) // false
利用instanceof运算符,还可以巧妙的解决,调用构造函数时,忘了加new命令的问题。
function Fubar(foo, bar) {
if (this instanceof Fubar) {
this.foo = foo
this.bar = bar
} else {
return new Fubar(foo, bar)
}
}
var f = Fubar(1,2)
var f1 = new Fubar(3,4)
console.log(f) //Fubar {foo:1,bar:2}
console.log(f1 instanceof Fubar) // true
构造函数的继承
让一个构造函数继承另一个构造函数,是非常常见的需求。这可以分成两步实现。第一步是在子类的构造函数中,调用父类的构造函数。
function Super() {}
function Sub(value) {
Super.call(this)
}
上面代码中,Sub是子类的构造函数,this是子类的实例。在实例上调用父类的构造函数Super,就会让子类实例具有父类实例的属性。
第二步,是让子类的原型指向父类的原型,这样子类就可以继承父类原型。
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.prototype.method = '...'
上面代码中,Sub.prototype是子类的原型,要将它赋值为Object.create(Super.prototype),而不是直接等于Super.prototype。否则后面两行对Sub.prototype的操作,会连父类的原型Super.prototype一起修改掉。
另外一个写法是Sub.prototype等于一个父类实例。
Sub.prototype = new Super()
上面这种写法也有继承效果,但是子类会具有父类实例的方法。有时,这可能不是我们需要的,所以不推荐这种写法。
function Shape() {
this.x = 0
this.y = 0
}
Shape.prototype.move = function (x, y) {
this.x += x
this.y += y
console.log('shape moved')
}
我们需要让Rectangle构造函数继承Shape。
// 第一步,子类继承父类的实例
function Rectangle() {
Shape.call(this)
}
// 另一种写法
function Rectangle() {
this.base = Shape
this.base()
}
// 第二步,子类继承父类的原型
Rectangle.prototype = Object.create(Shape.prototype)
Rectangle.prototype.constructor = Rectangle
采用这样的写法以后,instanceof运算符会对子类和父类的构造函数,都返回true。
var rect = new Rectangle()
console.log(rect instanceof Rectangle) // true
console.log(rect instanceof Shape) // true
上面代码中,子类是整体继承父类。有时只需要单个方法的继承,这时可以采用下面的写法。
function ClassA() {
}
ClassA.prototype.getName = function () {
console.log(this.name)
}
function ClassB(name) { this.name = name }
ClassB.prototype.getName = function () {
ClassA.prototype.getName.call(this)
//some code
}
var b = new ClassB('test')
b.getName() // test
上面代码中,子类B的getName方法先调用父类A的getName方法,再部署自己的代码。这就等于继承了父类A的getName方法。
多重继承
JavaScript不能提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。
function M1() {
this.hello = 'hello'
}
function M2() {
this.world = 'world'
}
function S() {
M1.call(this)
M2.call(this)
}
// 继承M1
S.prototype = Object.create(M1.prototype)
// 继承链上加入M2
Object.assign(S.prototype, M2.prototype)
// 制定构造函数
S.prototype.constructor = S
var s = new S()
console.log(s.hello) // hello
console.log(s.world) // world
上面代码中,子类S同时继承了父类M1和M2。这种模式又称为Mimin(混入)。