JS常见的七种继承方式
继承概念的探究
继承可以使得子类具有父类的各种方法和属性
第一种:原型链继承
原型链是较常见的继承方式之一,其中涉及的构造函数、原型和实例对象
-
每一个构造函数都有一个原型对象
-
原型对象又包含一个指向构造函数的指针
-
实例对象包含一个原型对象的指针
function Father() { this.name = 'Tom' this.arr = [1,2,3] } function Child() { this.age = 24 } //继承父类的属性和方法 但会导致共享问题 Child.prototype = new Father() const c1 = new Child() const c2 = new Child() c1.arr.push(4) console.log(c1.arr); //[ 1, 2, 3, 4 ] console.log(c2.arr); //[ 1, 2, 3, 4 ] /** 内存空间是共享的 当一个发生变化的时候 另外一个也随之进行了变化 */
第二种:构造函数继承(借助call)
function Father() {
this.name = 'Tom'
this.arr = [1,2,3]
}
Father.prototype.getName = function () {
return this.name
}
function Child() {
Father.call(this)
this.age = 24
}
const c1 = new Child()
const c2 = new Child()
c1.arr.push(4)
console.log(c1.arr); //[ 1, 2, 3, 4 ]
console.log(c2.arr); //[ 1, 2, 3]
//无法获取父类的方法
console.log(c1.getName()); //报错 c1.getName is not a function
第三种:组成继承(前两种组合)
function Father(name) {
this.name = name
this.arr = [1,2,3]
}
Father.prototype.getName = function () {
return this.name
}
function Child(name) {
// 第二次调用Father
Father.call(this,name)
this.age = 24
}
// 第一次调用 Father
Child.prototype = new Father()
// 手动挂上构造器,指向自己的构造函数
Child.prototype.constructor = Child
const c1 = new Child('Tom')
const c2 = new Child('Jack')
c1.arr.push(4)
console.log(c1.arr); //[ 1, 2, 3, 4 ]
console.log(c2.arr); //[ 1, 2, 3]
console.log(c1.getName()); // 'Tom'
console.log(c2.getName()); // 'Jack'
第四种:原型式继承
Object.create()
-
可用作新对象原型的对象
-
为新对象定义额外的属性的对象
用此方法实现继承对于对象属性为复杂数据类型(浅拷贝)时,会因为共享,导致不同的子类都能篡改其父类属性的数据
let parent4 = {
name: 'Tom',
friend: ['p1', 'p2', 'p3'],
getName: function() {
return this.name
}
}
let person4 = Object.create(parent4)
person4.name = 'Jack'
person4.friend.push('p4')
console.log(person4.name); // Jack
console.log(person4.getName());//[ 'p1', 'p2', 'p3', 'p4' ],
console.log(parent4.getName());//[ 'p1', 'p2', 'p3', 'p4' ],
console.log(person4.__proto__);
/**
{
name: 'Tom',
friend: [ 'p1', 'p2', 'p3', 'p4' ],
getName: [Function: getName]
}
*/
第五种:寄生式继承
该方法是使用原型式继承可以获得一份目标对象的浅拷贝
然后利用浅拷贝的能力再进行增强添加一些方法
let parent5 = {
name: 'Tom',
friend: ['p1', 'p2', 'p3'],
getName: function() {
return this.name
}
}
function clone(original) {
let clone = Object.create(original)
clone.getFriends = function() {
return this.friend
}
return clone
}
let person5 = clone(parent5)
console.log(person5.getName()); //Tom
person5.friend.push('p44')
console.log(parent5.friend); // [ 'p1', 'p2', 'p3', 'p44' ]
console.log(person5.friend); // [ 'p1', 'p2', 'p3', 'p44' ]
第六种:寄生组合式继承
function clone(parent, child) {
child.prototype = Object.create(parent.prototype)
child.prototype.constructor = child
}
function Parent(name) {
this.name = name
this.age = 24
this.friend = [1,2,3]
}
Parent.prototype.getName = function() {
return this.name
}
function Child(name) {
Parent.call(this,name)
this.friend.push(4)
}
clone(Parent, Child)
Child.prototype.getFriend = function() {
return this.friend
}
let child1 = new Child('Tom')
let fat = new Parent('Jack')
console.log(child1.name); // Tom
console.log(child1.getName());// Tom
console.log(child1.friend); // [ 1, 2, 3, 4 ]
console.log(fat.name);// Jack
console.log(fat.getName());// Jack
console.log(fat.friend); //[ 1, 2, 3 ]
console.log(child1.getFriend());
console.log(fat.getFriend()); // 报错 fat.getFriend is not a function
第七种:ES6的 extends 关键字实现逻辑
class Parent {
constructor(name) {
this.name = name
this.friend = [1,2,3]
}
getName = function() {
return this.name
}
}
class Person extends Parent {
constructor(name, age) {
//子类中存在构造函数,则需要在使用 this 之前,先调用super()
super(name)
this.age = age
}
}
const per = new Person('Tom', 26)
per.friend.push(4, 5)
const fat = new Parent('Jack')
console.log(per.friend);// [ 1, 2, 3, 4, 5 ]
console.log(per.getName());// Tom
console.log(fat.friend);// [ 1, 2, 3 ]