继承行为在 ES5 与 ES6 中的区别详解
笔者注:一句话引发的基础知识回炉,基础不扎实,还要什么自行车
最近在看React方面的一些文章时,看到了这样一个问题,「为什么每个class中都要写super,super是做什么的?」,刚看到这个问题时,直接就想到了继承行为在javascript中的表现。后面作者的一句话「super不可以省略,省略的话会报错」。当时脑海中蹦出来一个念头,这个同学是不是写错了,super不就是用来完成调用父类构造函数,将父类的实例属性挂在到this上吗?为什么不写还会报错?
后来自己亲自写了一个Demo尝试了一下,还真是会报错,到底是哪里出了问题,找到了阮老师的教程又打开仔细看了一遍,发现里面还真是有这样一句话:
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
原来如此,ES6中this对象的构造方式发生了变化。
ES5中的继承
//Shape-父类(superclass)
functionShape(){
this.x=0;
this.y=0;
}
//父类的方法
Shape.prototype.move=function(x,y){
this.x+=x;
this.y+=y;
console.info('Shapemoved.');
};
//Rectangle-子类(subclass)
functionRectangle(){
Shape.call(this);//callsuperconstructor.
}
//子类续承父类
Rectangle.prototype=Object.create(Shape.prototype);
Rectangle.prototype.constructor=Rectangle;
varrect=newRectangle();
console.log('IsrectaninstanceofRectangle?',
rectinstanceofRectangle);//true
console.log('IsrectaninstanceofShape?',
rectinstanceofShape);//true
rect.move(1,1);//Outputs,'Shapemoved.'
如上所示:展示了一个ES5中实现单继承的例子,在《Javascript高级程序设计》一书中,给这种继承方式定义为「寄生组合式继承」。不管什么形式,什么命名,在ES5中实现继承始终就是要坚持一个原则:将实例属性放在构造函数中挂在this上,将一些方法属性挂在原型对象上,子类可共享。上面这种继承方式的关键在于两点:
- 子类构造函数通过apply或者call的方式运行父类的构造函数,此举将父类的实例属性挂在子类的this对象上
- 以父类的原型对象为基础,与子类的原型对象之间建立原型链关系,使用了Object.create,本质在于Child.prototype.__proto===Parent.prototype;
ES6中的继承
classPoint{
constructor(x,y){
this.x=x;
this.y=y;
}
toString(){
return'('+this.x+','+this.y+')';
}
}
classColorPointextendsPoint{
constructor(x,y,color){
super(x,y);//调用父类的constructor(x,y)
this.color=color;
}
toString(){
returnthis.color+''+super.toString();
}
}
ES6中的继承使用到了extends关键字,function也变成了class关键字。class的本质还是一个语法糖,这个大家都会脱口而出,但是在继承机制这里到底是如何做到的,我们看一下babel在此处是如何帮我们转译的,
varColorPoint=
/*#__PURE__*/
function(_Point){
_inherits(ColorPoint,_Point);
functionColorPoint(x,y,color){
var_this;
_classCallCheck(this,ColorPoint);
_this=_possibleConstructorReturn(this,_getPrototypeOf(ColorPoint).call(this,x,y));//调用父类的constructor(x,y)
_this.color=color;
return_this;
}
_createClass(ColorPoint,[{
key:"toString",
value:functiontoString(){
returnthis.color+''+_get(_getPrototypeOf(ColorPoint.prototype),"toString",this).call(this);
}
}]);
returnColorPoint;
}(Point);
如上是经过babel转译后的代码,有几个关键点:
一、_inherits()
function_inherits(subClass,superClass){
if(typeofsuperClass!=="function"&&superClass!==null){
thrownewTypeError("Superexpressionmusteitherbenullorafunction");
}
subClass.prototype=Object.create(superClass&&superClass.prototype,{
constructor:{
value:subClass,
writable:true,
configurable:true
}
});
if(superClass)_setPrototypeOf(subClass,superClass);
}
首先完成extends对象的校验,必须是function或者null,否则报错。其次完成以下事情:
ColorPoint.__proto__===Point; ColorPoint.prototype.__proto__===Point.prototype;
二、ColorPoint构造函数中_classCallCheck(),_possibleConstructorReturn()
function_classCallCheck(instance,Constructor){
if(!_instanceof(instance,Constructor)){
thrownewTypeError("Cannotcallaclassasafunction");
}
}
主要是用来检测构造函数不能直接调用,必须是通过new的方式来调用。
function_possibleConstructorReturn(self,call){
if(call&&(_typeof(call)==="object"||typeofcall==="function")){
returncall;
}
return_assertThisInitialized(self);
}
调用父类的构造函数,初始化一些实例属性,并将this返回。使用该返回的this赋值给子类的this对象,子类通过这一步返回的this对象,再该基础之上在添加一些实例属性。
这就是最大的不同之处。如果不经历这一步,子类没有this对象,一旦操作一个不存在的this对象就会报错。
三、_createClass()
function_createClass(Constructor,protoProps,staticProps){
if(protoProps)_defineProperties(Constructor.prototype,protoProps);
if(staticProps)_defineProperties(Constructor,staticProps);
returnConstructor;
}
最后一步完成原型属性与静态属性的挂载,如果是原型属性,挂在在Constructor上的prototype上,如果是静态属性或者静态方法,则挂在Constuctor上。
总结
基础知识要打牢,不是为了面试,前期打不劳,后面很多事情就会变的模棱两可,别人问到的时候,就会是「可能」、「也许」。不积跬步何以至千里,加油。
参考链接
http://es6.ruanyifeng.com/#docs/class-extends
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
https://babeljs.io/repl/#?babili=false&evaluate=true&lineWrap=false&presets=es2015%2Creact%2Cstage-2&targets=&browsers=&builtIns=false&debug=false&code_lz=Q
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
