这是我参与8月更文挑战的第二十八天,活动详情查看:8月更文挑战
显式混入
由于JavaScript不会自动实现复制行为,所以我们需要手动实现复制功能。这个功能在许多的库和框架中被称为extend(),但是为了方便理解,我们称之为mixin()。
寄生继承
显式混入模式的一种变体被称为“寄生继承”,它既是显式的又是隐式的。
//“传统的JavaScript类”Vehicle
function Vehicle(){
this.engines = 1;
}
Vehicle.prototype.ignitions = function(){
console.log("Turn on my engine.");
};
Vehicle.prototype.drive = function(){
this.ignitions();
console.log("Steering and moving forward!");
};
//“寄生类”Car
function Car(){
//首先,car是一个Vehicle
var car = new Vehicle();
//然后对car进行定制
car.wheels = 4;
//保存到Vehicle::drive()的特殊引用
var vehDrive = car.drive;
//重写Vehicle::drive()
car.drive = function(){
vehDrive.call(this);
console.log("Rolling on all" + this.wheels + "wheels!");
}
return car;
}
var myCar = new Car();
myCar.drive();
//Turning on my engine.
//Steering and moving forward!
//Rolling on all 4 wheels!
首先我们复制了一份Vehicle父类(对象)的定义,然后混入子类(对象)的定义,如果需要的话保留到父类的特殊引用,然后用这个复合对象去构建实例。
隐式混入
隐式混入和前面提到的显式伪多态很像,因此也具备同样的问题:
var Something = {
cool: function (){
this.greeting = "hello world";
this.count = this.count? this.count + 1 : 1;
}
};
Something.cool();
Something.greeting;//hello world
Somethig.count;// 1
var Another = {
cool:function(){
//隐式把Something混入Another
Something.cool.call(this);
}
};
Another.cool();
Another.greeting;//hello world
Another.count;//1(count不是共享状态)
通过在构造函数调用或者方法调用中使用Something.cool.call(this),我们实际上借用了函数Something.cool()并在Another的上下文中调用了它(通过this绑定)。最终的结果是Something.cool()中的赋值操作都会应用在Another对象上而不是Something对象上。因此,我们把Something的行为“混入”到了Another中。虽然这类技术利用了this的重新绑定功能,但是Something.cool.call(this)仍然无法变成相对引用,所以仍需要小心使用。
小结
混入模式(无论是显式还是隐式)可以用来模拟类的复制行为,但是通常会产生丑陋并且脆弱的语法,比如显式伪多态(OtherObj.methodName.call(this,...)),这会让代码更加难懂并且难以维护。此外,显式混入实际上无法完全模拟类的复制行为,因为对象只能复制引用,无法复制被引用的对象或函数本身。