

#yyds干货盘点#JavaScript的几种继承方式
source link: https://blog.51cto.com/u_11365839/5453105
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

#yyds干货盘点#JavaScript的几种继承方式
原创几种继承方式
- 原型链继承
- 借助构造函数继承(经典继承)
- 组合继承:原型链 + 借用构造函数(最常用)
- 原型式继承 (Object.create)
- 寄生式继承
- 寄生组合式继承(最理想)
- ES6中的继承
原型链继承
子类型的原型为父类型的一个实例对象
this.name = 'bigStar';
this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
console.log(this.name)
}
function Child() {
this.subName = 'litterStar';
}
// 核心代码: 子类型的原型为父类型的一个实例对象
Child.prototype = new Parent();
let child1 = new Child();
let child2 = new Child();
child1.getName(); // bigStar
child1.colors.push('pink');
// 修改 child1.colors 会影响 child2.colors
console.log(child1.colors); // [ 'red', 'blue', 'yellow', 'pink' ]
console.log(child2.colors); // [ 'red', 'blue', 'yellow', 'pink' ]
注意核心代码:
Child.prototype = new Parent();
特点:
- 父类新增在构造函数上面的方法,子类都能访问到
缺点:
- 来自原型对象的所有属性被所有实例共享,child1修改 colors 会影响child2的 colors
- 创建子类实例时,无法向父类的构造函数传参
借助构造函数继承(经典继承)
在子类的构造函数中使用 call()或者 apply() 调用父类型构造函数
this.name = name;
this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
console.log(this.name)
}
function Child(name, age) {
// 核心代码:“借调”父类型的构造函数
Parent.call(this, name);
this.age = age;
}
let child1 = new Child('litterStar');
let child2 = new Child('luckyStar');
console.log(child1.name); // litterStar
console.log(child2.name); // luckyStar
// 这种方式只是实现部分的继承,如果父类的原型还有方法和属性,子类是拿不到这些方法和属性的。
child1.getName(); // TypeError: child1.getName is not a function
注意核心代码:
Parent.call(this, name);
特点:
- 避免引用类型的属性被所有实例共享
- 创建子类实例时,可以向父类传递参数
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性和方法
- 无法实现函数复用,每次创建实例都会创建一遍方法,影响性能
组合继承:原型链 + 借用构造函数(最常用)
this.name = name;
this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
console.log(this.name)
}
function Child(name, age) {
// 核心代码①
Parent.call(this, name);
this.age = age;
}
// 核心代码②: 子类型的原型为父类型的一个实例对象
Child.prototype = new Parent();
Child.prototype.constructor = Child;
// 可以通过子类给父类的构造函数传参
let child1 = new Child('litterStar');
let child2 = new Child('luckyStar');
child1.getName(); // litterStar
child2.getName(); // luckyStar
child1.colors.push('pink');
// 修改 child1.colors 不会影响 child2.colors
console.log(child1.colors); // [ 'red', 'blue', 'yellow', 'pink' ]
console.log(child2.colors); // [ 'red', 'blue', 'yellow' ]
注意核心代码:
const person1 = Object.create(person);
特点:
- 没有严格意义上的构造函数,借助原型可以基于已有对象创建新对象
缺点:
- 调用了两次父类构造函数,生成了两份实例
- 一次是设置子类型实例的原型的时候
Child.prototype = new Parent();
- 一次是创建子类型实例的时候
let child1 = new Child('litterStar');
, 调用 new 会执行Parent.call(this, name);
,此时会再次调用一次Parent
构造函数
原型式继承 (Object.create)
借助原型可以基于现有方法来创建对象,var B = Object.create(A) 以A对象为原型,生成A对象,B继承了A的所有属性和方法。
name: 'star',
colors: ['red', 'blue'],
}
// 核心代码:Object.create
const person1 = Object.create(person);
const person2= Object.create(person);
person1.name = 'litterstar';
person2.name = 'luckystar';
person1.colors.push('yellow');
console.log(person1.colors); // [ 'red', 'blue', 'yellow' ]
console.log(person2.colors); // [ 'red', 'blue', 'yellow' ]
注意核心代码:
const person1 = Object.create(person);
特点:
- 没有严格意义上的构造函数,借助原型可以基于已有对象创建新对象
缺点:
- 来自原型对象的所有属性被所有实例共享,person1修改 colors 会影响person2的 colors,这点跟原型链继承一样。
寄生式继承
创建一个用于封装继承过程的函数,该函数在内部以某种方式来增强对象
// 通过调用函数创新一个新对象
var clone = Object.create(original);
// 以某种方式来增强这个对象
clone.sayName = function () {
console.log('hi');
}
// 返回这个对象
return clone;
}
特点:
每次创建对象都会创建一遍方法,跟借助构造函数模式一样
寄生组合式继承(最理想的)
我们可以先回忆一下JavaScript最常用的继承模式: 组合继承(原型链 + 借用构造函数),它的最大缺点是会调用两次父构造函数(Child.prototype = new Parent();
和 let child1 = new Child('litterStar');
)。
我们是否可以想办法是调用一次?可以让 Child.prototype 访问到 Parent.prototype。
我们不能直接使用 Child.prototype = Parent.prototype
来实现,因为会出现一些副作用,你可能在修改 Child.prototype
的时候会修改Parent.prototype
。
可以使用 Object.create(...)
来实现
Object.create
MDN上的解释:它会创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
this.name = name;
this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
console.log(this.name)
}
function Child(name, age) {
// 核心代码①
Parent.call(this, name);
this.age = age;
}
// 核心代码②
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
注意核心代码:
Parent.call(this, name);
和 Child.prototype = Object.create(Parent.prototype);
寄生组合式继承,集寄生式继承和组合式继承的优点,是引用类型最理想的继承范式。
ES6 中class的继承
ES6中引入了class关键字,可以通过extends关键字实现继承。
class Child extends Parent {
constructor(name, age, color) {
// 调用父类的constructor(name, age)
super(name, age);
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。
- 赞
- 收藏
- 评论
- 分享
- 举报
Recommend
-
9
一、作用域环境 在js作用域环境中访问变量是由内向外的,内部作用域可以获得当前作用域下的变量,和当前作用域外层作用域下的变量 外层作用域无法访问内部函数的变量 不同的函数作用域中不能相互访问彼此间的变量 ...
-
6
#yyds干货盘点#THREE.BoxHelper的使用注意 原创 歆冉 2022-01-17 08:55:11...
-
9
1、Spring Data JPA1.1、pom<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"...
-
8
Windows 下搭建ActiveMQ环境 #yyds干货盘点# 原创 梁云亮 2022-01-17 11:25:27...
-
7
STP故障 1、根桥故障 在稳定的STP拓扑里面,非根桥会定期收到来自根桥的BPDU报文,如果根桥发生了故障,停止发送BPDU报文,下游交换机就无法收到来自根桥的BPDU报文。如果下游交换设备在MaxAge(default=20s)内没有收到BPDU报文,就会导致已经...
-
11
#yyds干货盘点# HashMap的4种遍历方式 原创 97的风 2022-02-18 10:29:22...
-
4
#yyds干货盘点#ABAP FORM参数传递方式 原创 Louie_Ma 2022-03-02 21:14:58...
-
5
作为前端开发工程师,JavaScript 是我们的主要开发语言,它本身语法比较简单,并且生态系统也非常完善,在社区的影响力越来越大。在我们使用过程中,经常会遇到各种奇怪的问题,让我们经常摸不着头脑。奇怪的 try..catch
-
3
#yyds干货盘点#Java中拼接String的几种方式 精选 原创 尼羲 2022-09-26 22:14:15
-
6
#yyds干货盘点#聊聊javascript——call、applay、bind 精选 原创 尼羲 2022-11-16 2...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK