1. 原型链继承
先回顾一下构造函数,原型和实例的关系:每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,实例都包含一个指向原型对象
的内部指针。如果我们让原型对象等于另一个类型的实例,就形成了一条原型链。
function SuperType() { this.name = ‘SuperType‘ } SuperType.prototype.sayNmae = function () { console.log(this.name) } function SubType() { this.remark = ‘SubType‘ } SubType.prototype = new SuperType() SubType.prototype.sayRemark = function () { console.log(this.remark) } var instance1 = new SubType() console.log(instance1 instanceof SubType) //true console.log(instance1 instanceof SuperType) //true console.log(instance1 instanceof Object) //true
讲再多都没有一张图来的明白 顺着__proto__就是我们说的原型链啦。另外超类型是指SuperType,子类型是指Subtype
原型链的问题
function SuperType() { this.colors = [‘red‘,‘yellow‘,‘blue‘] } function SubType() { } SubType.prototype = new SuperType() var instance1 = new SubType() var instance2 = new SubType() instance1.colors.push(‘green‘) console.log(instance1.colors)//["red", "yellow", "blue", "green"] console.log(instance2.colors)//["red", "yellow", "blue", "green"]
包含引用类型值的原型属性会被所有实例共享,这里在超类型中定义的colors原本是每个实例单独所有的,但是由于把SuperType的实例作为了SubType的prototype,
colors也就成了所有SubType实例所共享的了,所以修改一个实例colors,其他的实例也会改变
原型链的另一个问题就是创建自类型实例时,无法向超类型的构造函数中传值
2.借用构造函数
function SuperType(name) { this.name = name this.colors = [‘red‘,‘blue‘,‘green‘] } function SubType() { SuperType.apply(this,arguments) // 在子类型内部调用超类型 } var instance1 = new SubType(‘Alan‘) var instance2 = new SubType(‘Bob‘) instance1.colors.push(‘black‘) console.log(instance1.colors) //["red", "blue", "green", "black"] console.log(instance2.colors) //["red", "blue", "green"] console.log(instance1.name) //Alan console.log(instance2.name) //Bob
借用构造函数最重要的一步在于,在子类型内部调用超类型的构造函数,修改了this指向,同时满足了参数的传递
借用构造函数的问题
方法都在构造函数中定义,无法实现函数复用
3.组合继承
组合继承又叫经典继承,解决了原型链和借用构造函数的问题。
function SuperType(name,age) { this.name = name this.age = age this.colors = [‘red‘,‘blue‘,‘green‘] } Object.defineProperties(SuperType.prototype,{ sayName:{ value:function () { console.log(this.name) }, writable:true, configurable:true, enumerable:true } }) function SubType() { SuperType.apply(this,arguments) //第二次调用超类型构造函数 } SubType.prototype = new SuperType() //第一次调用超类型构造函数 var instance1 = new SubType(‘Banks‘,22) var instance2 = new SubType(‘Bob‘,33) instance1.colors.push(‘black‘) console.log(instance1.colors) //["red", "blue", "green", "black"] console.log(instance2.colors) //["red", "blue", "green"]
组合继承,在构造函数中定义非公用属性,在原型中定义公用方法。
组合继承的问题
如果说这种继承方式还有问题的话,那就是调用了两次超类型构造函数。
4.原型式继承
道格拉斯,克罗克福德(没错,就是那个提出稳妥对象的小伙伴)提出了原型式继承。他的想法是借助已有的对象创建新对象,同时不必因此创建自定义类型。
function object(o) { function F() { //相当于原型链继承中的自类型构造函数 } F.prototype = o // 这里的o相当于原型链继承中的超类型的实例 return new F() }
在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的新实例
和原型链继承有相似的地方,但是没有超类型构造函数,并且把一系列操作放封装在了object函数内部。从本质上讲,object()对其中的对象
执行了一次潜复制
var person = { name:‘Nicholas‘, age:22, friends:[‘Shelby‘,‘Court‘,‘Van‘] } var anotherPerson = object(person) anotherPerson.name = ‘Alan‘ anotherPerson.friends.push(‘Bob‘) var yetAnoterPerson = object(person) console.log(anotherPerson.name) //Alan console.log(anotherPerson.friends) //[‘Shelby‘,‘Court‘,‘Van‘,‘Bob‘] console.log(yetAnoterPerson.name) //Nicholas console.log(yetAnoterPerson.friends) //[‘Shelby‘,‘Court‘,‘Van‘,‘Bob‘]
原型式继承要有一个对象作为另一个对象的继承,然后将这个对象传递给object函数,然后根据需求对得到的对象进行修改
但是person对象中包含friends引用类型,这里的friends是共有的,这里在修改(注意是修改,而不是改变)
anotherPerson.friends = [‘Shelby‘] console.log(anotherPerson.friends) //["Shelby"] console.log(yetAnoterPerson.friends) //[‘Shelby‘,‘Court‘,‘Van‘,‘Bob‘]
上面的代码我们改变了anotherPerson的friends,这里会在anotherPerson实例中添加一个friends属性,并不会改动,person中的friends
原型链继承中也是一样的道理
原型式继承的问题
原型式继承创建的对象,相当于传入对象的副本,和原型链继承的问题一样,每个实例没有自定义的属性。而且原型式继承进行的是浅复制,无法用instanceof操作符检测。
关于instanceof操作符与原型链 参考这一篇
https://www.cnblogs.com/flawlessBlithe/p/8515435.html
5.寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象。
function createAnother(original) { var clone = object(original) clone.sayHi = function () { // 以某种方式增强对象 返回实例之前给实例添加方法 console.log(‘hi‘) } return clone }
寄生式继承的问题
寄生式继承解决了原型式继承无法自定义方法的问题,但是依然是进行的浅复制,无法进行instanceof操作符检测
6.寄生组合式继承
看名字大家就想到了,寄生组合式继承为的就是解决,寄生式继承无法进行instanceof操作符检测的问题,以及组合式继承调用了两次超类型构造函数的问题。
function object(o) { function F() { //相当于原型链继承中的子类型构造函数 } F.prototype = o // 这里的o相当于原型链继承中的超类型的实例 return new F() } function SuperType(name,age) { this.name = name this.age = age } SuperType.prototype.sayName = function () { console.log(this.name) } function SubType() { SuperType.apply(this,arguments) } // 下面这个函数做的操作为的是解决组合继承中第二次调用超类型构造函数的问题 // 即这一步 SubType.prototype = new SuperType() function inheritPrototype(SubType,SuperType) { var n = object(SuperType.prototype) //对超类型的原型进行浅复制 n.constructor = SubType // 如果没有这一步后面将无法使用instanceof检查是不是SubType的实例 /* 上面的操作是使用原型式继承的方式 创造一个SuperType.prototype的副本 也就是替换 new Supertype() 这一步 */ console.log( n.__proto__ === SuperType.prototype )// true console.log(n instanceof SuperType) //true SubType.prototype = n } inheritPrototype(SubType,SuperType) SubType.prototype.sayAge = function () { // 这里在prototype中添加sayAge方法已经是添加在了 inheritPrototype 函数内部的n上 console.log(this.age) } var instance = new SubType(‘Alan‘,23) console.log(instance instanceof SubType) //true console.log(instance instanceof SuperType) // true
寄生组合式继承 比较难理解的那里应该是解决第二次调用超类型构造函数的部分。
听说寄生组合式继承还有问题。。。。。 应该是 太麻烦了 哈哈哈