面向对象编程,少不了继承,js的es5的继承基础就是利用的原型,上面一篇文章已经理解了原型,现在来分析继承方式。
一、原型链继承
function Animal(type) { this.type = type; } Animal.prototype.colors = ['green','white','red'] Animal.prototype.toString = function () { console.log("I am parent") } function Cat(name) { this.name = name } Cat.prototype = new Animal(); Cat.prototype.getName = function () { console.log(this.name) } let cat1 = new Cat('sally'); cat1.colors.push('yellow') let cat2 = new Cat('kate') cat2.colors.push('blue') console.log(cat1.colors) console.log(cat2.colors)
从代码Cat.prototype = new Animal()可知,将父对象的实例赋给子对象的原型,实现了继承,这个从原型的原理可知道每个实例都有一条prototype属性指向创建这个实例对象的原型对象。但是这里有2个问题:(1)首先是没办法继承父构造函数里面的属性 (2)如果把引用类型数据放在原型对象里面,就会导致所有实例共享该对象了。
二、组合继承
组合继承是利用构造函数和原型共同组合成的继承,借用构造函数实现对实例属性的继承,通过原型对原型的属性和方法的继承。
Animal.prototype.getType = function () { console.log(this.type) } function Dog(name,type) { Animal.call(this,type) //第一次调用Animal() this.name = name; } Dog.prototype = new Animal(); //第二次调用Animal() Dog.prototype.constructor = Dog; Dog.prototype.getName = function () { console.log(this.name) } var dog = new Dog("dog","haha") dog.getName()
组合继承是最常用的继承模式,但是它也有缺陷,技术就是不断的探索与升华,这种组合继承会无论什么情况下,都会两次调用父构造函数。接着就出现了更好的继承方式,寄生组合式继承。
function Animal(type){ this.type = type } Animal.prototype.getType = function () { console.log(this.type) } function Dog(name,type) { Animal.call(this,type) this.name = name; } Dog.prototype.getName = function () { console.log(this.name) } var protoType = Object.create(Animal.prototype); protoType.constructor = Dog; Dog.prototype = protoType; Dog.prototype.getName = function () { console.log(this.name) } var dog = new Dog("dog","haha") dog.getName()
寄生式组合继承最精华的部分就是通过复制父构造函数的原型对象赋值给子构造函数的原型,当然,Object.create()是ES6的写法,可以实现一个create函数。
function create(o){ function F() {} F.prototype = o; return new F() }
三、class 类继承
class Animal{
constructor(){
this.name = name;
}
}
class Dog extends Animal{
constructor(){
super(name)
}
toString(){
console.log(this.name)
}
}
new Dog('aa');
这些实例类的属性都定义在constructor里面,类的继承通过babel转化为如下:
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 Animal() { _classCallCheck(this, Animal); this.name = name; }; var Dog = function (_Animal) { _inherits(Dog, _Animal); function Dog() { _classCallCheck(this, Dog); return _possibleConstructorReturn(this, (Dog.__proto__ || Object.getPrototypeOf(Dog)).call(this, name)); } _createClass(Dog, [{ key: 'toString', value: function toString() { console.log(this.name); } }]); return Dog; }(Animal);
主要是inherits这个函数来实现继承,里面是这样一段代码
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;