第二个问题是创建Son的实例时,不能向Father的构造函数中传递参数,也就是说,没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
接下来我要将的第二种继承方式是构造函数继承,它可以解决包含引用类型值所带来的问题。
2.构造函数继承 构造函数继承的概念实现构造函数继承的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。
让我们来看下面这段代码:
function Father() { this.cars = ['奔驰', '宝马', '兰博基尼']; } function Son() { // 继承Father Father.call(this); } const xiaoming = new Son(); xiaoming.cars.push('五菱宏光'); console.log(xiaoming.cars); //'奔驰, 宝马, 兰博基尼, 五菱宏光' const xiaohong = new Son(); console.log(xiaohong.cars); //'奔驰, 宝马, 兰博基尼'通过使用call()方法(或apply()方法),在创建xiaoming实例的同时,调用了Father构造函数,这样一来,就会在Son的实例对象上执行Father构造函数所定义的所有对象初始化代码,因此xiaoming和xiaohong就具有属于自己的cars属性了。
构造函数继承还有一个优点是可以给超类型构造函数传参,让我们来看下面这段代码。
function Father(name) { this.name = name; } function Son(name, age) { Father(this, name); this.age = age; } const xiaoming = new Son('小明', 19); console.log(xiaoming.name); //'小明' console.log(xiaoming.age); //19我们创建了xiaoming实例并传递两个参数name和age,name参数通过调用Father构造函数传递参数给了Father构造函数中的name,因此xiaoming实例拥有name和age两个实例属性。
构造函数继承的优点可以在子类型构造函数中向超类型构造函数传参;子类型构造函数创建的对象都拥有各自的属性和方法(引用类型)
构造函数继承的缺点很明显,方法都在构造函数中定义的话,函数复用就无从谈起了,因此构造函数继承很少单独使用。接下来介绍的这种继承方式,通过原型链和构造函数结合实现的继承,叫做组合继承。
3.组合继承 组合继承的概念组合继承的基本思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
使用组合继承的优点是即通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性,来看下面这段代码。
function Father(name) { this.name = name; this.cars = ['奔驰', '宝马', '兰博基尼']; } Father.prototype.sayName = function() { console.log(this.name); } function Son(name, age) { // 继承属性 Father.call(this, name); //第二次调用Father() this.age = age; } // 继承方法 Son.prototype = new Father(); //第一次调用Father() Son.prototype.constructor = Son; Son.prototype.sayAge = function() { console.log(this.age); } const xiaoming = new Son('xiaoming', 18); xiaoming.cars.push('五菱宏光'); console.log(xiaoming.cars); //'奔驰, 宝马, 兰博基尼, 五菱宏光' xiaoming.sayName(); //'xiaoming' xiaoming.sayAge(); //18 const xiaohong = new Son('xiaohong', 20); console.log(xiaohong.cars); //'奔驰, 宝马, 兰博基尼' xiaohong.sayName(); //'xiaohong' xiaohong.sayAge(); //20 console.log(xiaoming instanceof Son) //true console.log(xiaoming instanceof Father) //true console.log(xiaoming instanceof Object) //true 组合继承的优点组合继承避免了原型链继承和构造函数继承的缺陷,融合它们的优点,成为JavaScript中最常用的继承模式。
组合继承的缺点组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数。一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
4.原型式继承 原型式继承的概念原型式继承的就是借助原型可以基于已有的对象创建新对象。
我们来看下面这段代码。
function object(o) { function F() {} F.prototype = o; return new F(); } const person = { name: 'zhangsan', cars: ['奔驰', '宝马', '兰博基尼'] } const anotherPerson = object(person); anotherPerson.name = 'lisi'; anotherPerson.cars.push('五菱宏光'); console.log(anotherPerson.name); //'lisi' console.log(anotherPerson.cars); //'奔驰, 宝马, 兰博基尼, 五菱宏光' const yetAnotherPerson = object(person); yetAnotherPerson.name = 'wangwu'; console.log(yetAnotherPerson.name); //'wangwu' console.log(yetAnotherPerson.cars); //'奔驰, 宝马, 兰博基尼, 五菱宏光'object()实际上是对对象的一次浅复制,实现原型式继承的前提是要求你必须有一个对象可以作为另一个对象的基础。