继承
寄生组合继承
接着上节
这个时候就需要先介绍一个object函数了
Object.create(proto, propertiesObject)
Object.create(proto, propertiesObject)
参数一,需要指定的原型对象
参数二,可选参数,给新对象自身添加新属性以及描述器
标准的寄生组合继承
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name) {
this.sex = 'boy'
Parent.call(this, name)
}
// 与组合继承的区别
Child.prototype = Object.create(Parent.prototype)
var child1 = new Child('child1')
与组合继承的区别:
1.是有冗余的,也child.__proto__的指向下(Parent[[Prototype]])下有 this.name=undefined,而标准寄生组合继承没有冗余。
2.getName是在child.proto.__proto__下,也在parent.__proto__下。
我们使用了Object.create(Parent.prototype)创建了一个空的对象,并且这个对象的__proto__属性是指向Parent.prototype的。
Object.setPrototypeOf()方法
Object.setPrototypeOf()
静态方法可以将一个指定对象的原型(即内部的 [[Prototype]]
属性)设置为另一个对象或者 null。
语法
jsCopy to Clipboard
Object.setPrototypeOf(obj, prototype)
参数
要设置其原型的对象。
该对象的新原型(一个对象或 null)。
返回值
指定的对象。
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name) {
this.sex = 'boy'
Parent.call(this, name)
}
Object.setPrototype(Child.prototype,Parent.prototype)
Child.prototype原来的对象
Parent.prototype要继承的对象
function A(){
this.aaa = 'aaa'
}
function B(){
this.bbb = 'bbb'
A.apply(this)
}
function C(){
this.ccc = 'ccc'
B.apply(this)
}
Object.setPrototypeOf(B.prototype, A.prototype);
Object.setPrototypeOf(C.prototype, B.prototype);
c = new C()
// 再对比一下 Object.create的情况
function A(){}
function B(){
A.apply(this)
}
function C(){
B.apply(this)
}
B.prototype = Object.create(A.prototype);
C.prototype = Object.create(B.prototype);
c = new C()
对比:Object.create:c.__proto__的是A的实例化对象,没有constructor,但是可以人工指向
而Object.setPrototypeOf(B.prototype, A.prototype);c.__proto__是B的实例化对象,有constructor。
标准的寄生组合继承带有引用的情况下
function Parent(name){
this.name = name
this.face = 'cry'
this.colors = ['white', 'black']
}
Parent.prototype.features = ['cute']
Parent.prototype.getFeatures = function (){
console.log(this.features)
}
function Child(name){
Parent.call(this, name)
this.sex = 'boy'
this.face = 'smile'
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
var child1 = new Child('child1')
child1.colors.push('yellow')
var child2 = new Child('child2')
child2.features = ['sunshine']
console.log(child1)
console.log(child2)
child1.getFeatures()
child2.getFeatures()
通过child1和child2(colors)结果对比,解决了内存共享的问题,是一个深拷贝。
// 寄生组合继承算是ES6之前一种比较完美的继承方式。
// 它避免了组合继承中调用两次父类构造函数,初始化两次实例属性的缺点。
// 它拥有了上述所有继承方式的优点
// 只调用了一次父类构造函数,只创建了一份父类属性
// 子类可以用到父类原型链上的属性和方法
// 能够正常的使用instanceOf和isPrototypeOf方法
原型式继承
原理是创建一个构造函数,构造函数的原型指向对象,然后调用 new 操作符创建实例,并返回这个实例,本质是一个浅拷贝
var cat = {
heart: '❤️',
colors: ['white', 'black']
}
var guaiguai = Object.create(cat)
var huaihuai = Object.create(cat)
console.log(guaiguai)
console.log(huaihuai)
console.log(guaiguai.heart)
console.log(huaihuai.colors)
同样的,对于 setPrototypeOf
var cat = {
heart: '❤️',
colors: ['white', 'black']
}
res1 = {}
Object.setPrototypeOf(res1, cat);
两者并没有什么区别
寄生式继承 在原型式继承的基础之上进行了一下优化
var cat = {
heart: '❤️',
colors: ['white', 'black']
}
function createAnother (original) {
var clone = Object.create(original);
clone.actingCute = function () {
console.log('我是一只会卖萌的猫咪')
}
return clone;
}
var guaiguai = createAnother(cat)
var huaihuai = Object.create(cat)
guaiguai.actingCute()
console.log(guaiguai.heart)
console.log(huaihuai.colors)
console.log(guaiguai)
console.log(huaihuai)
这两种模式开发和逆向不常用
混入式继承(多继承)
我们一直都是以一个子类继承一个父类,而混入方式继承就是教我们如何一个子类继承多个父类的。也就是单线继承之前讲的是单线继承 A>B>C A继承B,B继承C,现在想A>B
>C
A继承B的同时又继承C
在这边,我们需要用到ES6中的方法Object.assign()
它的作用就是可以把多个对象的属性和方法拷贝到目标对象中,若是存在同名属性的话,后面的会覆盖前面
Object.assign()
Object.assign()
Object.assign()
静态方法将一个或者多个源对象 中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
function Parent (sex) {
this.sex = sex
}
Parent.prototype.getSex = function () {
console.log(this.sex)
}
function OtherParent (colors) {
this.colors = colors
}
OtherParent.prototype.getColors = function () {
console.log(this.colors)
}
function Child (sex, colors) {
Parent.call(this, sex)
OtherParent.call(this, colors) // 新增的父类
this.name = 'child'
}
Child.prototype = Object.create(Parent.prototype)
Object.assign(Child.prototype, OtherParent.prototype) // 新增的父类原型对象
Child.prototype.constructor = Child
var child1 = new Child('boy', ['white'])
child1.getSex()
child1.getColors()
console.log(child1)
修改方法
console.log(Child.prototype.proto === Parent.prototype)
console.log(Child.prototype.proto === OtherParent.prototype)
console.log(child1 instanceof Parent)
console.log(child1 instanceof OtherParent)
class中的继承
主要是依靠两个关键词:extends super
class Parent {
constructor (name) {
this.name = name
}
getName () {
console.log(this.name)
}
}
class Child extends Parent {
constructor (name) {
super(name)
this.sex = 'boy'
}
}
var child1 = new Child('child1')
console.log(child1 instanceof Child)
console.log(child1 instanceof Parent)
// 对比一下寄生组合继承
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name) {
this.sex = 'boy'
Parent.call(this, name)
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
var child1 = new Child('child1')
console.log(child1)
child1.getName()
console.log(child1 instanceof Child)
console.log(child1 instanceof Parent)
// class的继承方式完全满足于寄生组合继承
// extends从字面上来看还是很好理解的,对某个东西的延伸,继承。
// 如果不用super看看效果
class Parent {
constructor (name) {
this.name = name
}
getName () {
console.log(this.name)
}
}
class Child extends Parent {
// constructor (name) {
// super(name)
// this.sex = 'boy'
// }
sex = 'boy' // 实例属性sex放到外面来
}
var child1 = new Child('child1')
console.log(child1)
child1.getName()
// class可以通过extends关键字实现继承父类的所有属性和方法
// 若是使用了extends实现继承的子类内部没有constructor方法,则会被默认添加constructor和super。
class Parent {
constructor () {
this.name = 'parent'
}
}
class Child extends Parent {
constructor () {
// super(name) // 把super隐去
}
}
var child1 = new Child()
console.log(child1)
child1.getName()
// 报错,必须得在constructor中调用一下super函数
// super其实有两种用法,一种是当作函数来调用,还有一种是当做对象来使用。
// 之前那道题就是将它当成函数来调用的,而且我们知道在constructor中还必须得执行super()。
// 其实,当super被当作函数调用时,代表着父类的构造函数。
// 虽然它代表着父类的构造函数,但是返回的却是子类的实例,也就是说super内部的this指向的是Child。
class Parent {
constructor () {
console.log(new.target.name)
}
}
class Child extends Parent {
constructor () {
var instance = super()
console.log(instance)
console.log(instance === this)
}
}
var child1 = new Child()
var parent1 = new Parent()
console.log(child1)
console.log(parent1)
// super当成函数调用时,代表父类的构造函数,且返回的是子类的实例,也就是此时super内部的this指向子类。
// 在子类的constructor中super()就相当于是Parent.constructor.call(this)
// super当成函数调用时的限制: 子类constructor中如果要使用this的话就必须放到super()之后
// super当成函数调用时只能在子类的construtor中使用
class Parent {
constructor (name) {
this.name = name
}
}
class Child extends Parent {
constructor (name) {
this.sex = 'boy'
super(name)
}
}
var child1 = new Child('child1')
console.log(child1)
// 报错
// super当成对象来使用时
// 在子类的普通函数中super对象指向父类的原型对象
// 在子类的静态方法中super对象指向父类
class Parent {
constructor (name) {
this.name = name
}
getName () {
console.log(this.name)
}
}
Parent.prototype.getSex = function () {
console.log('boy')
}
Parent.getColors = function () {
console.log(['white'])
}
class Child extends Parent {
constructor (name) {
super(name)
super.getName()
}
instanceFn () {
super.getSex()
}
static staticFn () {
super.getColors()
}
}
var child1 = new Child('child1')
child1.instanceFn()
Child.staticFn()
console.log(child1)
// super当成对象调用父类方法时this的指向
class Parent {
constructor () {}
}
Parent.prototype.sex = 'boy'
Parent.prototype.getSex = function () {
console.log(this.sex)
}
class Child extends Parent {
constructor () {
super()
this.sex = 'girl'
super.getSex()
}
}
var child1 = new Child()
console.log(child1)
// ES6规定,通过super调用父类的方法时,super会绑定子类的this