js 中的继承
1. 原型链继承
概念:
让 父类的属性和方法 在 子类实例 的原型链上;
方法:
让 子类的原型 指向 父类的实例,并且把 父类的实例中的 constructor
指向子类;
子类.propotype = new 父类();
子类.propotype.constructor = 子类;
特性:
-
原型链继承 不像其他编程语言的继承一样(其他编程语言的继承一般是拷贝继承,就是 子类 继承 父类,会把父类的属性和方法都拷贝一份到子类中,供子类的实例使用);
而原型链继承是把 父类的原型 放到 子类实例的原型链上,子类实例可以使用
__proto__
的 查找机制完成调用; -
子类可以重写父类的属性方法,但是会影响到 父类的实例;
-
子类要添加原型方法时,要在实现继承后添加;
-
父类中私有 和 公有 的属性方法,都会变成 子类 的 公有属性 和 方法;
例子1:
// 父类
function A(x) {
this.x = x
}
// 父类的原型对象
A.prototype.getX = function() {
console.log(this.x)
};
// 子类
function B (y) {
this.y = y
}
// 让 子类的原型 = 父类的实例, 完成继承
B.prototype = new A(100)
// 让 父类的实例的 constructor 指向 子类
B.prototype.constructor = B;
B.prototype.getY = function () {
console.log(this.y)
}
// 生成实例
let b1 = new B(200)
console.log(b1)
然后他们的结构图片为:
例子2:
// 父类
function A() {
this.name = '你好'
this.age = 23
this.list = []
}
// 父类的原型方法1
A.prototype.height = 100
// 父类的原型方法2
A.prototype.push = function(p) {
this.list.push(p)
}
// 子类
function B (js) {
this.js = js
}
// 让 子类的原型 = 父类的实例
B.prototype = new A()
B.prototype.constructor = B
B.prototype.one = function () {
console.log('子类的one')
}
let b1 = new B('子实例1');
console.log(b1)
// 调用子类的原型对象方法
b1.one() // => 子类的one
// 修改 父类中的属性,是引用地址,所以会造成 影响
b1.push('子类添加1')
console.log(b1.list) // => ["子类添加1"]
let b2 = new B('子实例2')
console.log(b2.list) // => ["子类添加1"]
2. call继承
概念:
在 子类 内,使用 call()
,调用 父类函数,并改变父类中的 this
指向;指向 子类的实例;
在 子类 中把 父类当做普通函数执行,让 父类中的 this 执行子类的实例,相当于给 子类的实例添加了 私有属性 和 方法;
方法:
function 父类 (b) {
this.b = b
}
function 子类 (a) {
父类.call(this, b)
this.xx = a
}
特性:
- 只能继承父类的 私有属性和方法(因为是把 父类当做普通函数执行的 和 类的原型对象上的属性和方法没有关系);
- 父类的私有 属性 和 方法,变成 子类的私有 属性 和 方法;
例子:
// 父类
function A(x) {
this.x = x
}
// 父类的原型对象
A.prototype.getX = function() {
console.log(this.x)
}
// 子类
function B (y) {
// 执行普通函数 A,并把 A 中的 this 改为 子类的实例 b1
A.call(this, 100) // => b1.x = x => b1.x = 100
this.y = y
}
B.prototype.getY = function () {
console.log(this.y)
}
// 生成实例
let b1 = new B(200)
console.log(b1)
3. 寄生组件 继承
概念:
使用 call()
和 类似原型继承;使用 call()
继承父类的私有属性方法,利用 原型 Object.create(Obj)
继承 父类的公有属性方法;
方法:
function A (x) {
this.x = x
}
function B (y) {
A.call(this, 10)
this.y = y
}
// 利用 Object.create 创建一个空对象,把空对象的 __propo__ 指向 A的原型;
B.prototype = Object.create(A.prototype)
// 把空对象的 constructor 指向 子类;
B.prototype.constructor = B
特征:
- 父私有和公有的属性和方法,分别是 子类 私有 和 公有的属性和方法(推荐使用);
- 不过
Object.create()
在 ES6 中出现,IE 版本浏览器不支持;但我们可以手写一个Object.create
函数;注意,在 IE 里是不允许操作__proto__
的;
Object.create = function(obj) {
function Fn() {}
// 空对象 的原型指向 父类
Fn.prototype = obj;
// new 出来的实例,就的 __proto__ 就指向 obj(父类)了。
return new Fn();
}
例子:
// 父类
function A(x) {
this.x = x
}
// 父类的原型对象
A.prototype.getX = function() {
console.log(this.x)
}
// 子类
function B (y) {
// call 继承私有属性和方法
A.call(this, 100)
this.y = y
}
// Object.create 继承公有属性方法
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
/*
以上的继承和 原型链的继承差不多,
原型链的继承: 将 子类的原型 指向 父类的实例
寄生组合继承: 将 子类的原型 指向 新创建的对象(就像父类的实例,只不过他没有私有属性和方法,因为新创建的对象, __proto__ 也指向 父类的原型;
*/
B.prototype.getY = function () {
console.log(this.y)
}
// 生成实例
let b1 = new B(200)
console.log(b1)
他们的结构为:
4. ES6 中的 class 继承
概念:
类的继承使用 extends
和 super
关键字进行 类之间的继承;
类的继承不能使用 call()
和 子类.prototype = Object.create(父类.prototype)
方法:
class 父 {
constructor () {
// 私有属性和方法
}
// 公有方法
xx() {}
}
class 子 extends 父 {
constructor () {
super()
}
}
注意:
extends
的功能和 子.prototype.__proto__ = 父.prototype
一致 。
子类继承父类,可以不写 constructor
,如果有写了 constructor
那么他里面的第一条代码,就必须写 super()
;如果不写 comstructor
浏览器会自动创建
constructor (...xx) {
super(...xx)
}
super()
就相对于 call()
把父类当做普通函数执行,并向父类传递参数,让方法中的 this
指向 子类的实例 。
例子:
// 父类
class A {
constructor (x) {
// 私有属性
this.x = x
}
// 公有方法
getX () {
console.log(this.x)
}
}
// 也可以给原型添加属性或方法
A.prototype.goA = 'A变身'
// 子类
class B extends A{
/*
如果使用了 继承 extends,在子类的构造函数中要把 super() 写上,
因为他是传递数据给 父类 的,如果没写 super() 就会报错的,因为父类中,
有 待传入的参数 。*/
constructor (y) {
super() // 类似于 call()
this.y = y
}
getY () {
console.log(this.y)
}
}
let b1 = new B(100);
console.log(b1)
super()
不传时,父类中接收到的参数为: undefined
。
学习视频地址:哔哩哔哩视频传送门