在上一篇中我们看了没有继承的实现,这一篇我们看下继承的实现
继承的实现
还是用这个继承的例子:
class Animal {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class Dog extends Animal{
constructor(name) {
super(name);
this.name = name;
}
}
复制代码
我们babel一下,得到如下代码:
"use strict";
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof 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;
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Animal = function () {
function Animal(name) {
_classCallCheck(this, Animal);
this.name = name;
}
_createClass(Animal, [{
key: "getName",
value: function getName() {
return this.name;
}
}]);
return Animal;
}();
var Dog = function (_Animal) {
_inherits(Dog, _Animal);
function Dog(name) {
_classCallCheck(this, Dog);
var _this = _possibleConstructorReturn(this, (Dog.__proto__ || Object.getPrototypeOf(Dog)).call(this, name));
_this.name = name;
return _this;
}
return Dog;
}(Animal);
复制代码
Animal
的代码与上节非继承的方式一致,直接跳过,来看下最后一部分Dog
的代码:
// 这还是一个高阶函数,与没有继承的对象相比,这里多出了两个函数_inherits和_possibleConstructorReturn
var Dog = function (_Animal) {
// 继承函数,继承Animal的属性
_inherits(Dog, _Animal);
function Dog(name) {
_classCallCheck(this, Dog);
// 获取this
var _this = _possibleConstructorReturn(this, (Dog.__proto__ || Object.getPrototypeOf(Dog)).call(this, name));
_this.name = name;
return _this;
}
return Dog;
}(Animal);
复制代码
在来看_inherits
如何实现的:
// 继承函数
function _inherits(subClass, superClass) {
// 异常情况处理
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
// 将父函数构造器的prototype“拷贝”(使用原型链的方式并不是真正的赋值)一份给子函数构造器的prototype
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;
}
复制代码
这里面涉及到了subClass.__proto__
和subClass.prototype
,那么__proto__
和prototype
的区别是什么?
实际上__proto__
是真正查找时所用的对象,而prototype
是当你用new
关键在来构建对象时被用来建造__proto__
的,Object.getPrototypeof(dog) === dog.__proto__ === Dog.prototype
。
函数__possibleConstructorReturn
处理了构造函数有返回值的情况。这种情况下,需要改变this
使用该返回值作为this
。
// 构造函数有返回值的情况
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
复制代码
实际模拟
看了上面的实现,我们模拟这个步骤,为了简化我们省去错误处理和特殊情况。
var Animal = function(name) {
this.name = name;
}
Animal.prototype.getName = function() {
return this.name;
}
var Dog = function(name) {
Animal.call(this.name);
_this.name = name;
}
Dog.prototype = Animal.prototype;
复制代码
实现完成后发现,跟我们上一篇文章结尾,猜想实现的一样,这就很尴尬,本来觉得这种写法不太顺眼,看官方的支持,现在看起来就顺眼多了-_-。与完整实现相比我们缺少了一些原型赋值的步骤Dog.__proto__ = Animal
,但总体来说原理是一样的。