继承分类
- 基于原型链继承(把子类的prototype设置为父类的实例)
1 2 3 4 5 6 7 8
| // 父类 function Person() {}
// 子类 function Student(){}
// 继承 Student.prototype = new Person()
|
原型继承的缺点:如果属性是引用类型的话,会共享引用类型(引用属性会被所有实例共享)
1 2 3 4 5 6 7 8 9 10
| // 父类 function Person() { this.hobbies = ['music','reading'] }
// 子类 function Student(){}
// 继承 Student.prototype = new Person()
|
1 2 3 4 5 6 7
| var stu1 = new Student() var stu2 = new Student()
stu1.hobbies.push('basketball')
console.log(stu1.hobbies) // music,reading,basketball console.log(stu2.hobbies) // music,reading,basketball
|
- 构造函数继承(在子类中执行父类的构造函数)
1 2 3 4 5 6 7 8 9
| // 父类 function Person() { this.hobbies = ['muisc', 'writing', 'reading'] }
// 子类 function Student(){ Person.call(this) // 改动的代码 }
|
1 2 3 4 5
| var stu1 = new Student() var stu2 = new Student() stud1.hobbies.push('swimming') console.log(stud1.hobbies) // ['muisc', 'writing', 'reading', 'swimming'] console.log(stud2.hobbies) // ['muisc', 'writing', 'reading']
|
构造函数解决了引用类型被共享的问题,但是同时也导致了一个新的问题,每实例化一个子类,就复制了一次父类。
继承方式 | 继承核心代码 | 优缺点 |
---|
原型链继承 | Student.prototype = new Person() | 实例的引用类型共享 |
构造函数继承 | 在子类(Student)里执行 Person.call(this) | 实例的引用类型不共享 |
- 组合继承(普通函数使用构造函数继承,函数使用原型链继承)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // 父类 function Person() { this.hobbies = ['music','reading'] }
// 父类函数 Person.prototype.say = function() {console.log('I am a person')}
// 子类 function Student(){ Person.call(this) // 构造函数继承(继承属性) (第二次调用) } // 继承 Student.prototype = new Person() // 原型链继承(继承方法) (第一次调用)
|
1 2 3 4 5 6 7 8 9
| // 实例化 var stu1 = new Student() var stu2 = new Student()
stu1.hobbies.push('basketball') console.log(stu1.hobbies) // music,reading,basketball console.log(stu2.hobbies) // music,reading
console.log(stu1.say == stu2.say) // true
|
优点:
- 父类的方法可以被复用
- 父类的引用属性不会被共享
- 子类构建实例时可以向父类传递参数
缺点:
- 调用了两次父类的构造函数,第二次调用覆盖了第一次调用的同名参数。这种被覆盖的情况造成了性能上的浪费。
- 寄生式继承
核心:使用原型式继承获得一个目标对象的浅复制,然后增强这个浅复制的能力
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function createAnother(original){ var clone=object(original); //通过调用函数创建一个新对象 clone.sayHi = function(){ //以某种方式来增强这个对象 alert("hi"); }; return clone; //返回这个对象 }
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] };
var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
|
- 寄生组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); // 创建了父类原型的浅复制 prototype.constructor = subType; // 修正原型的构造函数 subType.prototype = prototype; // 将子类的原型替换为这个原型 }
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; }
SuperType.prototype.sayName = function(){ alert(this.name); };
function SubType(name, age){ SuperType.call(this, name); this.age = age; } // 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费 inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); }
|
- ES6 class extends
核心:es6的继承结果和寄生组合继承相似,但是,寄生组合继承是先创建子类实例this对象,然后再对其进行加强;而es6先将父类实例对象的属性和方法,加到this上面,然后又再使用子类的构造函数修改this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class A { }
class B { }
Object.setPrototypeOf = function (obj, proto) { obj.__proto__ = proto; return obj; }
// B 的实例继承 A 的实例 Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性 Object.setPrototypeOf(B, A);
|
https://juejin.im/post/58f94c9bb123db411953691b