黑马程序员技术交流社区

标题: 【西安校区】javascript继承总结 [打印本页]

作者: 逆风TO    时间: 2020-4-15 10:05
标题: 【西安校区】javascript继承总结
ES5中的继承

一 原型链继承

原型链继承的原理:直接让子类的原型对象指向父类实例,当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承。

[JavaScript] 纯文本查看 复制代码
// 父类
function fat () {
        this.name = 'zaq'
}
// 父类上的原型方法
fat.prototype.getName = function() {
        return this.name
}
// 子类
function child () {}
// 子类的原型对象方法指向父类 子类中找不到的属性方法就会向父类找
child.prototype = new fat()
// 不影响继承 顺便绑一下constructor
child.prototype.constructor = child
// child实例就可以访问父类及其原型上的属性和方法了
const Child = new child()
Child.name // 'zaq'
Child.getName() // 'zaq'

原型链继承的缺点

1、所有的child实例原型都指向同一个fat实例,因此对child实例修改某个父类引用类型会影响所有的child实例;
2、创建子类时无法向父类传参,没有实现super()的功能

[JavaScript] 纯文本查看 复制代码
function fat () {
        this.name = 'zaq'
}

fat.prototype.getName = function() {
        return this.name
}

function child () {}
child.prototype = new fat()
child.prototype.constructor = child
//
const Child1 = new child();
const Child2 = new child();
Child1.name = 'lsq'
console.log(Child1.name) // lsq
// 本来父类name属性是zaq 然后现在==
console.log(Child2.name) // lsq

二 构造函数继承

构造函数继承:在子类的构造函数中执行父类的构造函数,并为其绑定子类的this。让父类的构造函数把成员的属性和方法都挂在子类的this上,这样既避免了共用一个原型实例,又能像父类构造函数传参。

[JavaScript] 纯文本查看 复制代码
function fat (name) {
        this.name = name
}
fat.prototype.getName = function() {
        return this.name
}
function Child () {
        fat.call(this, 'zaq')
}
const child1 = new Child()
const child2 = new Child()
child1.name = 'xyc'
console.log(child1.name) // xyc
console.log(child2.name) // zaq

构造函数继承的缺点

继承不到父类上的属性和方法

[JavaScript] 纯文本查看 复制代码
child1.getName() // Uncaught TypeError: child1.getName is not a function

三 组合式继承
既然原型链继承和构造函数继承都有各自的缺点但是又能互补,那何不将两者结合起来使用;

[JavaScript] 纯文本查看 复制代码
function fat (name) {
        this.name = name
}
fat.prototype.getName = function() {
        return this.name
}
// 构造函数继承
function Child () {
        fat.call(this, 'zaq')
}
// 原型链继承
Child.prototype = new fat()
Child.prototype.constructor = Child

const child1 = new Child()
const child2 = new Child()
child1.name = 'lsq'
console.log(child1.name) // lsq
console.log(child2.name) // zaq
child1.getName() // lsq

组合式继承的缺点

每次创建子类实例都执行了两次构造函数(fat.call()和new fat()),虽然不影响继承,但是在子类创建时 原型中会存在两份相同的属性和方法。


四 寄生式组合继承

为了解决构造函数被执行两次的问题,我们将指向父类实例 变成转向父类原型,减少一次构造函数的执行

[JavaScript] 纯文本查看 复制代码
function fat (name) {
        this.name = name
}
fat.prototype.getName = function() {
        return this.name
}
// 构造函数继承
function Child () {
        fat.call(this, 'zaq')
}
// 原型链继承
Child.prototype = fat.prototype // 将指向父类实例转成转向父类原型
Child.prototype.constructor = Child

const child1 = new Child()
const child2 = new Child()
child1.name = 'lsq'
console.log(child1.name) // lsq
console.log(child2.name) // zaq
child1.getName() // lsq

但是这种方法也存在一个问题,就是子类和父类的原型都指向同一个对象,如果我们对子类原型操作就会对父类原型产生影响。比如给子类Child.prototype新增一个getName方法,那么父类fat.prototype也增加或是被覆盖一个getName方法。为了解决这个问题 我们给fat.prototype做一个浅拷贝;

[JavaScript] 纯文本查看 复制代码
function fat (name) {
        this.name = name
}
fat.prototype.getName = function() {
        return this.name
}
// 构造函数继承
function Child () {
        fat.call(this, 'zaq')
}
// 原型链继承
Child.prototype = Object.create(fat.prototype) // 将指向父类实例转成转向父类原型
Child.prototype.constructor = Child

const child1 = new Child();
const child2 = new fat();
child1.getName() // zaq
child2.getName() // undefined

到这里我们就完成了es5里的继承,有空更新es6的继承方法


ES6中的继承

class介绍 来自阮一峰

es6中的继承:

1、class可以理解为functon,由于class本质还是一个function,因此它也有一个prototype属性。当new一个class时,会把class的protortype属性赋值给这个新对象的_proto_属性上;

2、constructor是默认添加的方法,在new一个对象的时候,会自动调用该方法, constructor里定义自己的属性;

3、继承extends和super, class子类名 extends父类名 实现继承。当然,还得在constructor里写上super (父类的参数),意思就是在子类中获取父类的this指针,相当于fat.call(this),参考

[JavaScript] 纯文本查看 复制代码
// 父类
class fat {
        constructor(props) {
                this.name = props || '';
        }
        getName () {
                return this.name
        }
}
// 继承
class Child extends fat {
        constructor(props, attrs) { // props继承父类的属性,attrs自己私有的属性
                super(props); // 相当于获取父类的this指向
                this.rename = props.rename || ''
                this.attrs = attrs
        }
        // 子类自己的方法
        getFatname () {
                return this.name
        }
        getAttrs () {
                return this.attrs
        }
}
// 通过new实例化一个构造函数
const child1 = new Child({
        name: 'zaq',
        rename: 'zwj'
}, 'mine attrs')
child1.getName() // zaq
child1.getRename() // zwj
child1.getAttrs() // mine attrs






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2