对象A通过继承B对象,就能直接拥有B对象的所有属性和方法,这对于代码的复用是非常有用的
1.原型链 继承
function Parent() {this.name = "chu"}Parent.prototype.getName = function () {console.log(this.name)}function Child() {}Child.prototype = new Parent()Child.prototype.constructor = Childconst child = new Child()console.log(child.getName()) //chu
问题:
1.引用类型(对象)的属性被所有实例共享,例子
function Parent() {this.names = ["chu", "chu1"]}function Child() {}Child.prototype = new Parent()var child1 = new Child()child1.names.push("yayu")console.log(child1.names) //["chu", "chu1", "yayu"]var child2 = new Child()console.log(child2.names) //["chu", "chu1", "yayu"]
2.在创建 Child 的实例时,不能向 Parent 传参
2.借用构造函数(经典继承 )
就是子类构造函数中使用 apply 或 call 调用父类构造函数
本来,父类构造函数中的 this 是父类的实例,但是这里,在子类构造函数中通过 call(this) 调用了父类构造函数,把上下文修改为了子类实例,相当于把父类实例的属性给子类实例复制了一份
function Parent() {this.names = ["chu", "chu1"]}//子类本身没有实例属性 但是借用了父类实例function Child() {Parent.call(this)}var child1 = new Child()child1.names.push("yayu")console.log(child1.names) //["chu", "chu1", "yayu"]var child2 = new Child()console.log(child2.names) // ["chu", "chu1"]
优点:
1.避免了引用类型的属性被所有实例共享
2.可以在Child 中向Parent 传参
function Parent(name) {this.name = name}function Child(name) {Parent.call(this, name)}var child1 = new Child("kevin")console.log(child1.name)var child2 = new Child("daisy")console.log(child2.name)
缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法
补充:
缺点:父类原型对象中一旦存在父类之前自己定义的方法,那么子类将无法继承这些方法。
只能继承父类的实例属性和方法,不能继承原型属性或方法。
见下图
function Parent1() {this.name = "parent1";}Parent1.prototype.getName = function () {return this.name;};function Child1() {Parent1.call(this);this.type = "child1";}let child = new Child1();console.log(child); // 没问题console.log(child.getName()); // 会报错


3.组合继承
就是原型链继承+借用构造函数
- 使用借用构造函数的方法,复制一份父类实例p的属性到子类实例c上
- 使用原型链的方法,把子类实例挂到原型链上,使得子类实例也能够访问父类原型对象上的属性和方法 ```javascript function Parent(name){ this.name = name this.colors = [“red”,”blue”,”green”] }
Parent.prototype.getName = function(){ console.log(this.name) }
function Child(name,age){ //第二次调用Parent() Parent.call(this,name) this.age = age } //第一次调用Parent() Child.prototype = new Parent() //手动挂上构造器,指向自己的构造函数 Child.prototype.constructor = Child
var child1 = new Child(“kevin”,18) child1.colors.push(“black”) console.log(child1.name) console.log(child1.age) console.log(child1.colors)
var child2 = new Child(“daisy”,20)
优点:融合原型链继承和构造函数的优点<br />缺点:因为使用了原型链,一个子类实例将会持有两份父类实例的数据,一份是 Parent.call(this) 复制到子类实例c上的数据,一份是父类实例原本的数据,位于 c._ _proto__ 上。<br />补充:Parent执行了两次,第一次是改变Child的prototype的时候,第二次是通过call方法调用Parent多构造一次就多进行了一次性能开销。<a name="yg6cP"></a>## 4.原型式继承ES5 `Object.create` 的模拟实现,将传入的对象作为创建的对象的原型创建一个空对象,并把它挂载到另一个对象的原型链上```javascriptfunction createObj(o){function F(){}F.prototype = oreturn new F()}
缺点:包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样
function createObj(o){function F(){}F.prototype = oreturn new F()}var person = {name:"kevin",friends:["daisy","kelly"]}var person1 = createObj(person)var person2 = createObj(person)console.log(person1) //F {}//注意:修改person1.name的值,person2.name的值并未发生改变,并不是因为person1和person2有独立的 name 值,而是因为person1.name = 'person1',给person1添加了 name 值,并非修改了原型上的 name 值。person1.name = "person1"console.log(person2.name) //值类型person1.friends.push("taylor")console.log(person2.friends) // ["daisy", "kelly", "taylor"]
5.寄生式继承
原型式继承的增强版。使用原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力再进行增强,添加一些方法。
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj(o){//使用了前面的 createObject 函数,生成了一个子类实例var clone = Object.create(o)//先在子类实例上添加一点属性或方法clone.sayName = function(){console.log("hi")}//再返回return clone}
let parent5 = {name: "parent5",friends: ["p1", "p2", "p3"],getName() {return this.name;},};function clone(original) {let clone = Object.create(original);clone.getFriends = function () {return this.friends;};return clone;}let person5 = clone(parent5);console.log(person5.getName());console.log(person5.getFriends());
6.寄生组合式继承(常用)
借用构造函数继承 + 寄生式继承
组合继承的缺点,会有两份父类实例的数据。这两份数据中,通过Parent.call(this) 复制到子类实例 c 上的这一份是真正需要的,而 c. proto 上的这一份是多余的,是原型链的副作用。
优化:把子类实例添加到父类实例的原型链上,同时又不让父类实例的属性和方法也在原型链上
- 创建一个没有实例属性的父类实例
- 让子类实例绕过父类实例,直接继承父类的原型对象
function Parent(name) {this.name = namethis.colors = ["red", "blue", "green"]}Parent.prototype.getName = function () {console.log(this.name)}function Child(name, age) {Parent.call(this, name)this.age = age}var child1 = new Child("kevin", 18)console.log(child1)

封装
function object(o){function F(){}F.prototype = oreturn new F()}function prototype(child,parent){var prototype = object(parent.property)prototype.constructor = childchild.prototype = prototype}prototype(Child,Parent)
例子
function clone(parent,child){child.prototype = Object.create(parent.prototype)child.prototype.constructor = child}function Parent6(){this.name = 'parent6'this.play = [1,2,3]}Parent6.prototype.getName = function(){return this.name}function Child6(){Parent6.call(this)this.friends = 'child5'}clone(Parent6,Child6)Child6.prototype.getFriends = function(){return this.friends}let person6 = new Child6()console.log(person6)console.log(person6.getName())console.log(person6.getFriends())
ES6 extends
https://es6.ruanyifeng.com/#docs/class-extends
class Person {constructor(name) {this.name = name;}// 原型方法// Person.prototype.getName=function(){}getName() {console.log(this.name);}}class Gamer extends Person {constructor(name, age) {//子类中存在构造函数 则需要在使用this之前调用super()super(name);this.age = age;}}const asuna = new Gamer("Asuna", 20);asuna.getName();
文章
https://github.com/mqyqingfeng/Blog/issues/16
