继承
# js 如何实现继承
- 借助 call
- 借助原型链
- 前两种结合
- 组合继承的优化 1
- 组合继承的优化 2(推荐)
- ES6 的 extends 被编译后的 JavaScript 代码
# 借助 call
function Parent() {
this.name = "parent";
}
function Child() {
Parent.call(this);
this.type = "child";
}
console.log(new Child());
问题:父类对象中的方法子类无法继承,只能拿到父类的属性值
# 借助原型链
function Parent() {
this.name = "parent";
this.play = [1, 2, 3];
}
function Child() {
this.type = "child";
}
Child2.prototype = new Parent();
console.log(new Child());
问题:引用类型指向的是同一个内存地址
# 前两种结合
function Parent() {
this.name = "parent";
this.play = [1, 2, 3];
}
function Child() {
this.type = "child";
Parent.call(this);
}
Child.prototype = new Parent();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);
问题:Parent 的构造函数会多执行了一次(Child.prototype = new Parent();)
# 组合继承的优化 1
function Parent() {
this.name = "parent";
this.play = [1, 2, 3];
}
function Child() {
Parent.call(this);
this.type = "child";
}
Child.prototype = Parent.prototype;
问题:将父类原型对象直接给到子类,父类构造函数只执行一次,而且父类属性和方法均能访问
但是子类实例的构造函数是 Parent4,显然这是不对的,应该是 Child4
var s3 = new Child();
var s4 = new Child();
console.log(s3);
# 组合继承的优化 2(推荐)
这是最推荐的一种方式,接近完美的继承,它的名字也叫做寄生组合继承
function Parent() {
this.name = "parent";
this.play = [1, 2, 3];
}
function Child() {
Parent5.call(this);
this.type = "child";
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
# ES6 的 extends 被编译后的 JavaScript 代码
es6 的 extends 经过 babel 编译之后的代码
function _possibleConstructorReturn(self, call) {
// ...
return call && (typeof call === "object" || typeof call === "function")
? call
: self;
}
function _inherits(subClass, superClass) {
// ...
//看到没有
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true,
},
});
if (superClass)
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: (subClass.__proto__ = superClass);
}
var Parent = function Parent() {
// 验证是否是 Parent 构造出来的 this
_classCallCheck(this, Parent);
};
var Child = (function(_Parent) {
_inherits(Child, _Parent);
function Child() {
_classCallCheck(this, Child);
return _possibleConstructorReturn(
this,
(Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments)
);
}
return Child;
})(Parent);
核心是_inherits 函数,可以看到它采用的依然也是第五种方式————寄生组合继承方式,同时证明了这种方式的成功。不过这里加了一个 Object.setPrototypeOf(subClass, superClass),这是用来干啥的呢?
答案是用来继承父类的静态方法。这也是原来的继承方式疏忽掉的地方。
继承的最大问题在于:无法决定继承哪些属性,所有属性都得继承。组合,这也是当今编程语法发展的趋势,比如 golang 完全采用的是面向组合的设计方式
# 参考链接
编辑 (opens new window)
上次更新: 2024/11/29, 10:10:04