学习链接
继承方式
1.原型链继承
关键
Child.prototype = new Parent();
完整
function Parent() {this.name = 'kevin';}Parent.prototype.getName = function () {console.log(this.name);}function Child() {}Child.prototype = new Parent();const child1 = new Child();child1.getName(); // kevin
问题
- 引用类型的属性被所有实例共享(
this.arr = [1,2]) - 创建
Child实例时,无法向Parent传参
补充
Child.constructor === FunctionChild.prototype.constructor === Parentchild1.constructor === Parent
2.盗用构造函数继承
关键
Parent.call(this);
完整
function Parent() {this.names = ['kevin', 'daisy'];this.getName = function() {console.log(this.names);}}function Child() {Parent.call(this);}const child1 = new Child();child1.names.push('yayu');child1.getName(); // ["kevin", "daisy", "yayu"]const child2 = new Child();child2.getName(); // ["kevin", "daisy"]
优点:
避免了引用类型的属性被所有实例共享
可以在
Child中向Parent传参
缺点:
子类不能访问父类原型上定义的方法
方法都在构造函数中定义,每次创建实例都会创建一遍方法
3.原型链 + 盗用构造函数的组合继承
关键
Child.prototype = new Parent();Parent.call(this);
完整
function Parent() {this.names = ['kevin', 'daisy'];}Parent.prototype.getName = function() {console.log(this.names);}function Child() {Parent.call(this);}Child.prototype = new Parent();const child1 = new Child();child1.names.push('yayu');child1.getName(); // ["kevin", "daisy", "yayu"]const child2 = new Child();child2.getName(); // ["kevin", "daisy"]
优点
- 融合原型链继承和盗用构造函数继承的优点,是 JavaScript 中最常用的继承模式。
缺点
- 调用了两次父类构造函数,生成了两份实例
4.原型式继承
关键
function createObj(o) {function F() {}F.prototype = o;return new F();}// 或Object.create(o);
完整
const person = {name: 'kevin',friends: ['daisy', 'kelly']}const person1 = createObj(person);const person2 = createObj(person);person1.name = 'person1'; // 定义在自身 覆盖了原型同名属性console.log(person2.name); // kevinperson1.friends.push('taylor');console.log(person2.friends); // ["daisy", "kelly", "taylor"]
优点
- 原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合
缺点
- 包含引用类型的属性始终都会共享相应的值,这点跟原型链继承一样
5.寄生式继承
关键
function createObj(o) {const clone = Object.create(o); // 共享了属性clone.sayName = function() {console.log(this.name);}return clone;}
完整
const person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};const anotherPerson = createObj(person);anotherPerson.sayName(); // "Nicholas"
优点
- 适合主要关注对象,而不在乎类型和构造函数的场景
缺点
- 跟借用构造函数模式一样,每次创建对象都会创建一遍方法
6.寄生组合式继承
关键
function createObj(o) {function F() {}F.prototype = o;return new F();}// 或Object.create(o);
function inheritPrototype(child, parent) {const prototype = createObj(parent.prototype);prototype.constructor = child;child.prototype = prototype;}
完整
function Parent() {this.names = ['kevin', 'daisy'];// console.log('调用了 Parent 构造函数');}Parent.prototype.getName = function() {console.log(this.names);}function Child() {Parent.call(this);}// Child.prototype = new Parent(); // 这里调用了 Parent 构造函数// 对比// const F = function () {};// F.prototype = Parent.prototype; // 这里没调用 Parent 构造函数// Child.prototype = new F();inheritPrototype(Child, Parent);const child1 = new Child();child1.names.push('yayu');child1.getName(); // ["kevin", "daisy", "yayu"]const child2 = new Child();child2.getName(); // ["kevin", "daisy"]
优点
这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。
与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
Class 继承
class Person {//调用类的构造方法constructor(name, age) {this.name = name;this.age = age;}//定义一般的方法showName() {console.log("调用父类的方法")console.log(this.name, this.age);}}let p1 = new Person('kobe', 39);console.log(p1);//定义一个子类class Student extends Person {constructor(name, age, salary) {super(name, age); //通过super调用父类的构造方法this.salary = salary;}showName() { //在子类自身定义方法console.log("调用子类的方法");console.log(this.name, this.age, this.salary);}}let s1 = new Student('wade', 38, 1000000000);console.log(s1);s1.showName();
补充
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。
Object.assign ( target, ...sources )将所有可枚举属性的值从一个或多个源对象分配到目标对象,返回目标对象
function M1() {this.hello = 'hello';}function M2() {this.world = 'world';}function S() {M1.call(this);M2.call(this);}// 继承 M1S.prototype = Object.create(M1.prototype);// 继承链上加入 M2Object.assign(S.prototype, M2.prototype);// 指定构造函数S.prototype.constructor = S;var s = new S();s.hello // 'hello's.world // 'world'
上面代码中,子类 S 同时继承了父类 M1 和 M2。这种模式又称为 Mixin(混入)。
注意:M2 是在继承链上,而非原型链。
