这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战
前言
今天我们来讲一讲盗用的构造函数吧。
盗用构造函数
为了解决原型包含引用值导致的继承问题,一种叫作 “盗用构造函数”( constructor stealing )的技术在开发社区流行起来(这种技术有时也称作“对象伪装”或“经典继承”)。基本思路很简单:在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply()和call()方法以新创建的对象为上下文执行构造函数。来看下面的例子:
function SuperType(){
this.colors = ["red", "blue", "green"];
function SubType ()//继承SuperType
SuperType .call(this);
let instance1 = new SubType() ;
instancel .colors.push("black") ;
console. log(instance1.colors) ; // "red, blue,green, black"
let instance2 = new SubType() ;
console. log (instance2.colors) ; // "red, blue, green"
示例中加粗的代码展示了盜用构造函数的调用。通过使用call()(或app1y() )方法,SuperType构造函数在为SubType的实例创建的新对象的上下文中执行了。这相当于新的SubType对象上运行了SuperType ()函数中的所有初始化代码。结果就是每个实例都会有自己的colors属性。
传递参数
相比于使用原型链,盗用构造函数的一个优点就是 可以在子类构造函数中向父类构造函数传参。来看下面的例子:
function SuperType (name){
this .name = name;
function SubType() {
//继承SuperType并传参
SuperType.call(this, "Nicholas");
//实例属性
this.age = 29;
let instance = new SubType();
console. log(instance .name); // "Nicholas";
console. log(instance,age); //29
在这个例子中,SuperType构造函数接收一个参数name,然后将它赋值给一个属性。 在SubType构造函数中调用superype构造函数时传人这个参数,实际上会在SubType的实例,上定义name属性。为确保SuperType构造函数不会覆盖Subrype定义的属性,可以在调用父类构造函数之后再给子类实例添加额外的属性。
盗用构造函数的问题
盗用构造两数的主要缺点,也是使用构造函数模式自定义类型的问题:必须在构造函数中定义方法, 因此函数不能重用。此外,子类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式。由于存在这些问题,盗用构造函数基本上也不能单独使用。
组合继承
组合继承(有时候也叫伪经典继承)综合了原型链和盗用构造函数,将两者的优点集中了起来。基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。来看下面的例子:
function SuperType (name) {
this.name = name ;
this.colors = ["red", "blue", "green"] ;
SuperType . prototype. sayName = function() {
console. log(this .name) ;
function SubType (name, age) {//继承属性
SuperType.call (this, name) ;
this.age = age;
//继承方法
SubType.prototype = new SuperType() ;
SubType . prototype. sayAge = function() {
console. log(this.age) ;
);
let instancel = new SubType ("Nicholas", 29) ;
instance1.colors.push("black") ;
console.log (instancel.colors) ;
// "red, blue, green, black"instance1. sayName() ;
// "Nicholas" ;
instance1.sayAge() ;
// 29
let instance2 = new SubType("Greg", 27);
console.log (instance2.colors); // "red, blue, green"instance2.sayName() ;
/ /"Greg";
instance2.sayAge() ;
// 27
在这个例子中,SuperType 构造函数定义了两个属性,name 和colors,而它的原型上也定义了一个 方法叫sayName ()。SubType构造函数调用了SuperType构造函数,传入了name参数,然后又定义了自己的属性age。此外,SubType . prototype也被赋值为SuperType的实例。原型赋值之后,又在这个原型上添加了新方法sayAge()。这样,就可以创建两个SubType实例,让这两个实例都有自己的属性,包括colors,同时还共享相同的方法。 组合继承弥补了原型链和盗用构造函数的不足,是JavaScript中使用最多的继承模式。而目组合继承也保留了instanceof操作符和isPrototypeOf()方法识别合成对象的能力。