js的继承方法
Posted 小顺石
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js的继承方法相关的知识,希望对你有一定的参考价值。
1、原型链的继承
// 原型链继承
/**
* 核心:将父类的实例作为子类的原型
* 优点:方法复用
* 由于方法定义在父类的原型上,复用了父类的构造函数方法,比如say方法
* 缺点:
* 创建子类实例的时候,不能向父类穿参数
* 子类实例共享了父类的构造函数的引用属性,比如arr属性
* 无法实现多继承
*
*
* */
function Parent(name){
this.name = name || \'父类\' // 实例的基本属性(该属性强调私有,不共享)
this.arr = [1] // 该属性强调私有,不共享
}
Parent.prototype.say = function (){ // 定义在原型对象上的方法(强调复用,需要共享)
console.log(\'hello\')
}
function Child(like){
this.like = like
}
Child.prototype = new Parent() // 核心,此时Child.prototype.constructor === Parent
console.log( Child.prototype.constructor === Parent) // true
// 一个完整的原型对象必须有constructor
Child.prototype.constructor = Child // 修正constructor 指向
let boy1 = new Child()
let boy2 = new Child()
// 优点:共享了父类构造函数的say方法
console.log(boy1.say()) // hello
console.log(boy2.say()) // hello
console.log(boy1.say() === boy2.say()) // true
// 缺点1 不能传参
console.log(boy1.name) // 父类
console.log(boy2.name) //父类
console.log(boy1.name === boy2.name) // true
// 缺点2 子类实例共享了父类的引用属性 比如arr
boy1.arr.push(2)
// 修改了boy1的arr ,影响了boy2的arr
console.log(boy2.arr) // [1,2]
2、借用构造函数继承
// 借用构造函数继承
/**
* 核心:借用父类的构造函数,增强子类的实例,等于复制父类的实例属性给子类
* 优点
* 1,可以传参
* 2,子类实例不共享父类的引用属性
* 3,可以是多继承 通过call继承多个父类
*
*
* 缺点
* 1,父类的方法不能复用
* 由于父类的方法在构造函数中,导致方法不能复用,每一次创建实例,都会生成一个方法
* 2,子类实例,继承不了父类原型的属性 没有用到原型
* */
function Parent(name){
this.name = name || \'父类\' // 实例的基本属性(该属性强调私有,不共享)
this.arr = [1] // 该属性强调私有,不共享
this.say = function(){
console.log(\'hello\')
}
}
function Child(name,like){
Parent.call(this,name)
this.like = like
}
let boy1 = new Child(\'小红\',\'apple\')
let boy2 = new Child(\'小明\',\'orange\')
// 优点1 可传参
console.log(boy1.name) //小红
console.log(boy2.name)// 小明
// 优点2,不共享父类的引用属性
boy1.arr.push(2)
console.log(boy1.arr,boy2.arr) // [ 1, 2 ] [ 1 ]
// 缺点1 不能共享父类的方法
console.log(boy1.say === boy2.say) // false 说明方法不同
// 缺点2 不继承父类原型上的方法
Parent.prototype.walk = function(){
console.log(\'walk\')
}
boy1.walk; // undefined
3.组合继承
/**
* 核心:通过调用父类构造函数,继承父类的属性并保留传参的优点;
* 然后通过将父类实例作为子类原型,实现函数复用。
* 优点:
* 1.保留构造函数的优点:创建子类实例,可以向父类构造函数传参数。
* 2.保留原型链的优点:父类的实例方法定义在父类的原型对象上,可以实现方法复用。
* 3.不共享父类的引用属性。比如arr属性
* 缺点:
* 由于调用了2次父类的构造方法,会存在一份多余的父类实例属性
*
* */
function Parent(name) {
this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // --- 将需要复用、共享的方法定义在父类原型上
console.log(\'hello\')
}
function Child(name,like) {
Parent.call(this,name,like) // 核心 第二次
this.like = like;
}
Child.prototype = new Parent() // 核心 第一次
let boy1 = new Child(\'小红\',\'apple\')
let boy2 = new Child(\'小明\',\'orange\')
// 优点1:可以传参数
console.log(boy1.name,boy1.like); // 小红,apple
// 优点2:可复用父类原型上的方法
console.log(boy1.say === boy2.say) // true
// 优点3:不共享父类的引用属性,如arr属性
boy1.arr.push(2)
console.log(boy1.arr,boy2.arr); // [1,2] [1] 没有共享arr属性。
console.log(boy1.constructor); // Parent 你会发现实例的构造函数是Parent。
Child.prototype.constructor = Child;
4、组合继承的优化
// 核心:
// 通过这种方式,砍掉父类的实例属性,这样在调用父类的构造函数的时候,就不会初始化两次实例,避免组合继承的缺点。
// 优点:
// 1.只调用一次父类构造函数。
// 2.保留构造函数的优点:创建子类实例,可以向父类构造函数传参数。
// 3.保留原型链的优点:父类的实例方法定义在父类的原型对象上,可以实现方法复用。
// 缺点:
// 1.修正构造函数的指向之后,父类实例的构造函数指向,同时也发生变化(这是我们不希望的)
function Parent(name) {
this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // --- 将需要复用、共享的方法定义在父类原型上
console.log(\'hello\')
}
function Child(name,like) {
Parent.call(this,name,like) // 核心
this.like = like;
}
Child.prototype = Parent.prototype // 核心 子类原型和父类原型,实质上是同一个
let boy1 = new Child(\'小红\',\'apple\')
let boy2 = new Child(\'小明\',\'orange\')
let p1 = new Parent(\'小爸爸\')
// 优点1:可以传参数
console.log(boy1.name,boy1.like); // 小红,apple
// 优点2:
console.log(boy1.say === boy2.say) // true
// 缺点1:当修复子类构造函数的指向后,父类实例的构造函数指向也会跟着变了。
// 具体原因:因为是通过原型来实现继承的,Child.prototype的上面是没有constructor属性的,就会往上找,这样就找到了Parent.prototype上面的constructor属性;当你修改了子类实例的construtor属性,所有的constructor的指向都会发生变化。
// 没修复之前:
console.log(boy1.constructor); // Parent
// 修复代码:
Child.prototype.constructor = Child
// 修复之后:
console.log(boy1.constructor); // Child
console.log(p1.constructor);// Child 这里就是存在的问题(我们希望是Parent)
5.寄生组合继承 --- 完美方式
function Parent(name) {
this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // --- 将需要复用、共享的方法定义在父类原型上
console.log(\'hello\')
}
function Child(name,like) {
Parent.call(this,name,like) // 核心
this.like = like;
}
Child.prototype = Object.create(Parent.prototype) // 核心 通过创建中间对象,子类原型和父类原型,就会隔离开。不是同一个啦,有效避免了方式4的缺点。
Child.prototype.constructor = Child
let boy1 = new Child(\'小红\',\'apple\')
let boy2 = new Child(\'小明\',\'orange\')
let p1 = new Parent(\'小爸爸\')
// 注意:这种方法也要修复构造函数的
// 修复代码:
Child.prototype.constructor = Child
// 修复之后:
console.log(boy1.constructor); // Child
console.log(p1.constructor);// Parent 完美
以上是关于js的继承方法的主要内容,如果未能解决你的问题,请参考以下文章