ES6时代的来临,使得类继承变得如此的圆滑。可是,你有思考过ES6的类继承模式吗?如何去实现它呢?git
类继承对于JavaScript来讲,实现方式与Java等类语言大不相同。熟悉JavaScript的开发者都清楚,JavaScript是基于原型模式的。那么,在es6没有出来以前,js是如何继承的呢?这是一个很是有意思的话题。但愿带着疑问看文章,或许对你的提高会更加巨大。若是你喜欢个人文章,欢迎评论,欢迎Star~。欢迎关注个人github博客es6
其实,js模拟一个类的方式很是的简单——构造函数。或许,这是全部人都在广泛使用的方式。咱们先来看一个例子:github
function Person(name){ this.name = name; } Person.prototype.sayName = function(){ console.log(this.name); } const person = new Person('zimo'); person.sayName(); //zimo
这里经过构造函数模拟出来的类,其实和其余语言的类行为上是基本一致的,惟一的区别就是它不具有私有方法。并且,他们一样是经过new操做符来进行实例化的,可是js的new操做与其它语言的new操做又会有所不一样。比方说:app
const person = Person('zimo'); console.log(person) //undefined
构造函数前面没有加new的状况下,会致使这个对象没有返回值来进行赋值。可是,在类语言中,在类名前不使用new是会报错的。函数
下面,咱们应该进一步来看一下js的new操做符的原理,以及实现。工具
new方法原理:this
js代码实现部分:spa
const person = new Person(args); //至关于 const person = Person.new(args); Function.prototype.new = function(){ let obj = new Object(); obj.__proto__ = this.prototype; const ret = this.apply(obj, arguments); return (typeof ret == 'object' && ret) || obj; }
到此为止,js如何去模拟类,咱们已经讲述完了。接下来,咱们应该看一下如何去实现相似与其余语言的类继承模式。prototype
尽管ES6已经对extends关键词进行了实现,可是原理性的知识,咱们应该须要明白。code
先来看一个场景,不管是狗或者是猫,它们都有一个共同的类animal,如图:
在真实开发中,咱们必须去实现类与类之间的继承关系,否则的话,咱们就必须重复地去命名构造函数(这样的方式是丑陋的)。
因此,像上述的场景,开发过程当中多的数不胜数,可是本质都是不变的。接下来,那咱们以一个例子来作说明,而且明白大体是如何去实现的。
例子:咱们须要去构造一个交通工具类,该类具有属性:轮子、速度和颜色(默认为黑),它还具有方法run(time)返回距离。以后,咱们还须要去经过该类继承一个‘汽车’类和一个‘单车’类。
如图:
实现:
function Vehicle(wheel, speed){ //首先构造一个交通工具类 this.wheel = wheel; this.speed = speed; this.color = 'black'; } Vehicle.prototype.run = function(time){ //在它的原型上定义方法 return this.speed * time; } function Car(wheel, speed, brand){ //经过在汽车类中去调用父类 Vehicle.call(this, wheel, speed); this.brand = brand; } Car.prototype = new Vehicle(); //将汽车类的原型指向交通工具的实例 function Bicycle(wheel, speed, owner){ //一样,构造一个自行车类,在其中调用父类 Vehicle.call(this, wheel, speed); this.owner = owner; } Bicycle.prototype = new Vehicle(); //将其原型指向交通工具实例 const car = new Car(4, 10, 'baoma'); const bicycle = new Bicycle(2, 5, 'zimo'); console.log(car.run(10)); //100 console.log(bicycle.run(10)); //50
这样子,就实现了类的继承。
大体的思路是:在继承类中调用父类,以及将继承类的原型赋值为父类的实例。
可是,每次实现若是都是这样子的话,又会显得很是的累赘,咱们并无将能够重复使用的部分。所以,咱们须要将不变的部分进行封装,封装成一个方法,而后将可变的部分当中参数传递进来。
接下来,咱们来分析一下类继承的封装方法extend。
首先,咱们来看一下,咱们须要实现怎样的继承:
function Animal(name){ //构造一个动物类 this.name = name; } Animal.prototype.sayName = function(){ console.log('My name is ' + this.name); } /** extends方法其中包含子类的constructor、自身的属性、和来自父元素继承的属性 */ var Dog = Animal.extends({ //使用extends方法来实现类的封装 constructor: function(name, lan){ this._super(name); this.lan = lan }, sayname: function(){ this._super.sayName(); }, sayLan: function(){ console.log(this.lan); } }); var animal = new Animal('animal'); var dog = new Dog('dog', '汪汪汪'); animal.sayName(); //My name is animal dog.sayName(); // My name is dog dog.sayLan(); // '汪汪汪'
其中的extend方法是咱们须要去实现的,在实现以前,咱们能够来对比一下ES6的语法
class Animal { constructor(name){ this.name = name; } sayName(){ console.log('My name is ' + this.name); } } /** 对比上面的extend封装和es6的语法,咱们会发现,其实差别并无太大 */ class Dog extends Animal{ constructor(name, lan){ super(name); this.lan = lan; } sayName(){ super.sayName(); } sayLan(){ console.log(this.lan); } }
其实,不少地方是类似的,比方说super和this._super。这个对象实际上是看起来是父构造函数,由于他能够直接调用this._super(name),但它同时还具有父构造函数原型上的函数,所以咱们能够把它称为父包装器。可是,必须保证的是_super中的函数对象上下文必须都是指向子构造函数的。
使用一张简陋的图来表示整个关系的话,如图:
下面咱们来实现一下这个extend方法。
Function.prototype.extend = function(props){ var Super = this; var Temp = function(){}; Temp.prototype = Super.prototype; var superProto = new Temp(); //去建立一个指向Super.prototype的实例 var _super = function(){ //建立一个父类包装器 return Super.apply(this, arguments); } var Child = function(){ if(props.constructor){ props.constructor.apply(this, arguments); } for(var i in Super.prototype){ _super[i] = Super.prototype[i].bind(this); //确保Super的原型方法拷贝过来时,this指向Child构造函数 } } Child.prototype = superProto; //将子类的原型指向父类的纯实例 Child.prototype._super = _super; //构建一个引用指向父类的包装器 for(var i in props){ if( i !== 'constructor'){ Child.prototype[i] = props[i]; //将props中方法放到Child的原型上面 } } return Child; }
继承的一些内容就分析到这里。其实,自从ES6标准出来以后,类的继承已经很是广泛了,由于真心好用。可是,也是愈来愈有人不懂得如何去理解这个继承的原理了。其实ES6中的继承的实现,也是挺简单的。
若是你对我写的有疑问,能够评论,如我写的有错误,欢迎指正。你喜欢个人博客,请给我关注Star~呦。你们一块儿总结一块儿进步。欢迎关注个人github博客