如果之前你问我ES5继承和ES6继承有什么区别,我会很自信的说没有区别,只是语法糖而已,充其量只是写法上的区别,不过现在我会假装想一想,然后说虽然只是语法糖,但是还是有一点区别的,那么具体的区别是什么,不要走开,下面更精彩!本文将先回顾一下ES5的寄生组合继承的实现,再看看ES6的写法,最后根据Babel编译结果看区别。ES5:寄生组合继承js有很多继承方式,比如我们熟悉的原型链继承,结构继承,组合继承,寄生继承等等,但是这些或多或少都有缺点,所以我觉得我们只要记住就够了以一种方式生存,即寄生结合遗传。首先要明确继承是什么继承。一共有三部分,一是实例属性/方法,二是原型属性/方法,三是静态属性/方法。让我们分别看看它们。我们看一下我们要继承的父类的函数://parentclassfunctionSup(name){this.name=name//实例属性}Sup.type='午'//静态属性//static方法同上。sleep=function(){console.log(`Iamsleeping${this.type}`)}//实例方法Sup.prototype.say=function(){console.log('Mynameis'+this.name)}Inheritinginstanceattributes/methods为了继承实例属性/方法,执行Sup函数并修改其this点是显而易见的,这可以通过使用call或apply方法来完成://SubclassfunctionSub(name,age){//继承父类的实例属性Sup.call(this,name)//自己的实例属性this.age=age}能够做到这一点的原理又是一道经典的面试题:new运算符是做什么用的do,很简单,就4点:1.创建一个空对象2.将对象的__proto__属性指向Sub.prototype3.让构造函数中的this指向新对象,然后执行构造函数,4.返回对象soSup.call(this)的this引用了新创建的对象,那么父类的实例属性/方法会被添加到the对象。我们都知道,如果一个对象没有方法,它就会去找它的构造函数的原型对象,也就是__proto__指向的对象。如果一直没有找到,就会去构造函数在原型对象的__proto__上搜索,这样一层层往上走,也就是传说中的原型链,所以如果Sub实例想要访问到Sup的原型方法,需要将Sub.prototype和Sup.prototype关联起来,有以下几种方式:1.使用Object.createSub.prototype=Object.create(Sup.prototype)Sub.prototype.constructor=Sub2。使用__proto__Sub.prototype.__proto__=Sup.prototype3。借用中间函数functionFn(){}Fn.prototype=Sup.prototypeSub.prototype=newFn()Sub.prototype.constructor=Sub以上三个方法都有,我们重写继承的Say方法,然后调用parent在这个方法中类原型上的say方法:Sub.prototype.say=function(){console.log('Hello')//调用父类的原型方法//this.__proto__===Sub.prototype,Sub.prototype.__proto__===Sup.prototypethis.__proto__.__proto__.say.call(this)console.log(`今年${this.age}岁`)}继承静态属性/方法就是继承Sup函数本身的属性和属性方法,这个很简单,遍历父类本身的可枚举属性,然后给子类添加:Object.keys(Sup).forEach((prop)=>{Sub[prop]=Sup[prop]})ES6:使用类继承接下来我们使用ES6的class关键字来实现上面的例子://ParentclassclassSup{constructor(name){this.name=name}say(){console.log('Myname'+this.name)}staticsleep(){console.log(`Iamsleeping${this.type}`)}}//static只能设置静态方法,不能设置静态属性,需要自己在Sup类中添加Sup.type='Noon'//另外类中不能设置原型属性,需要在原型上手动设置,如sup.prototype.xxx='xxx'//子类,继承父类classSubextendsSup{constructor(name,age){super(name)this.age=age}say(){console.log('Hello')super.say()console.log(`今年${this.age}岁`)}}Sub.type='lazy'可以看到同样的效果,使用class会简洁明了很多,那我们使用babel将这段代码编译回ES5语法,看看和我们写的有什么不同。由于编译后的代码有200多行,不能一下子全部贴上去。先从父类说起:编译后的父类//父类varSup=(function(){functionSup(name){_classCallCheck(this,Sup);this.name=name;}_createClass(Sup,[{key:“说”,价值:功能说(){console.log(“我打电话”+this.name);},},],[{键:“睡眠”,价值:功能睡眠(){控制台。日志("\u6211\u5728\u7761".concat(this.type,"\u89C9"));},},]);returnSup;})();//static只能设置静态方法,不能设置静态属性Sup.type="午";//子类,继承父类//如果我们之前通过sup.prototype.xxx='xxx'设置prototype属性,那么它和static属性是一样的,编译后没有区别,也是这样设置的,可以看到是一个自执行函数,定义了一个Sup函数,Sup首先调用了一个_classCallCheck(this,Sup)函数,我们去这个函数中看看:thrownewTypeError("Cannotcallaclassasafunction");}}instanceof操作符用来检测右边函数的prototype属性是否出现在左边对象的原型链上,简单来说就是可以判断一个对象是否是构造函数的实例,可以看如果不是,会抛出错误,错误信息是一个类不能是called作为一个函数。这里我们发现了第一个区别:区别一:ES5中的构造函数是普通函数,可以直接用new或者Call调用,而ES6中的class不能像普通函数一样直接调用,必须用new操作符调用继续看自执行函数,然后调用一个_createClass方法:如果(staticProps)_defineProperties(构造函数,staticProps);returnConstructor;}该方法接收三个参数,分别是构造函数、原型方法和静态方法(注意不包括原型属性和静态属性),后两者是数组,数组中的每一项代表一个方法对象,不管是实例方法还是原型方法,都是通过_defineProperties传递的方法设置,先看这个方法:function_defineProperties(target,props){for(vari=0;i
