继承
寄生组合继承
接着上节
这个时候就需要先介绍一个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