ES6中的class是如何实现的(附Babel编译的ES5代码详解)
序言
这篇文章主要讲解面试中的一个问题-ES6中的class语法的实现?
ECMAScript6实现了class,class是一个语法糖,使得js的编码更清晰、更人性化、风格更接近面向对象的感觉;也使IDE、编译期类型检查器、代码风格检查器等工具更方便地检测代码语法,做静态分析。同样的,这给没有类就缺点什么的软件开发工程师一个更低的门槛去接触js。
ES6class的ES5代码实现
JavaScript语言的传统方法是通过构造函数定义并生成新对象,这种写法和传统的面向对象语言差异较大。所以,ES6引入了Class这个概念作为对象的模板。
constructor
效果:ES6创建一个class会默认添加constructor方法,并在new调用时自动调用该方法。
ES5:
functionPerson(name,age){ this.name=name; this.age=age; } Person.prototype.toString=function(){ return'('+this.name+','+this.age+')'; } varp=newPerson('Mia',18); console.log(p);//Person{name:'Mia',age:18}
ES6:
classPerson{ constructor(name,age){ this.name=name; this.age=age; } toString(){ return'('+this.name+','+this.age+')'; } } varp=newPerson('Mia',18); console.log(p);//Person{name:'Mia',age:18}
ES6的class中constructor是构造方法,对应的是ES5中的构造函数Person,this关键字则代表实例对象。
里面的class类可以看做是构造函数的另一种写法,由typeofPerson==='function'为true;Person===Person.prototype.constructor为true可以得出,类的数据类型就是函数,类本身指向构造函数。也可以说class的底层依然是function构造函数。
类的公共方法都定义在类的prototype属性上。可以使用Object.assign一次向类添加多个方法。
特别的:class的内部定义的方法都是不可枚举的(non-enumerable),这一点与ES5的行为不一致。
ES5:
Object.keys(Person.prototype);//['toString']
ES6:
Object.keys(Person.prototype);//Person{}
不可枚举的代码实现会在后面将ES6代码用Babel转码之后解析。
new调用
效果:class类必须使用new调用,否则会报错。
ES5:
Person()//undefined
ES6:
Person()//TypeError:ClassconstructorPersoncannotbeinvokedwithout'new'
实例的属性
效果:实例的属性是显式定义在this对象上,否则都是定义在原型上。类的所有实例共享一个原型对象,与ES5行为一致。
ES5:
functionPerson(){ this.grade={ count:0 }; }
ES6:
classPerson{ constructor(){ this.grade={ count:0 }; } }
此外还要关注新提案,Babel已经支持实例属性和静态属性新的写法。
静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
注意:如果静态方法包含this关键字,指的是类。
ES5:
functionPerson(){} Person.toSay=function(){ return'IloveJavaScript.'; }; Person.toSay();//IloveJavaScript.
ES6:
classPerson{ statictoSay(){ return'IloveJavaScript.'; } } Person.toSay();//IloveJavaScript.
getter和setter
ES6提供get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为,和ES5行为一致。
ES5:
functionPerson(name){} Person.prototype={ getname(){ return'mia'; }, setname(newName){ console.log('newname:'+newName); } }
ES6:
classPerson{ getname(){ return'mia'; } setname(newName){ console.log('newname:'+newName); } }
ES6class底层实现原理
下文主要用babel转码器分别对class中几个主要的方法进行转码,分析ES5的实现方式。
Babel是如何编译class的
将下面的代码使用babel转码器转换成ES5代码,按照代码结构和功能分块进行讲解。
classPerson{ constructor(name,age){ this.name=name; this.age=age; } toString(){ return'('+this.name+','+this.age+')'; } } varp=newPerson('Mia',18);
运行模式
"usestrict";//class默认开启严格模式
私有函数:
JS开发者在变量名或函数名前缀加下划线,一般表示私有。
前缀加下划线表示私有仅仅是一个约定俗成的习惯,澄清意图,并没有做其他处理。由于ECMAScript草案中并没有定义私有变量的方法,所以在此限定之下仍可以在函数外或作用域外访问该函数或变量。
_instanceof和_classCallCheck的作用
检查声明的class类是否通过new的方式调用,否则会报错。
function_instanceof(left,right){ if(right!=null&&typeofSymbol!=="undefined"&&right[Symbol.hasInstance]){ returnright[Symbol.hasInstance](left); }else{ returnleftinstanceofright; } } function_classCallCheck(instance,Constructor){ if(!_instanceof(instance,Constructor)){ thrownewTypeError("Cannotcallaclassasafunction"); } }
_createClass和_defineProperties的作用
_createClass函数有三个参数,Constructor是传入构造函数Person,protoProps是要添加到原型上的函数数组,staticProps是要添加到构造函数本身的函数,即静态方法。这里的第二个和第三个参数是可以缺省的,会在_createClass函数体内判断。
_createClass函数的作用是收集公有函数和静态方法,将方法添加到构造函数或构造函数的原型中,并返回构造函数。
defineProperties是将方法添加到构造函数或构造函数的原型中的主要逻辑,遍历函数数组,分别声明其描述符。若enumerable没有被定义为true,则默认为fals,设置configurable为true。以上两个布尔值是为了限制Object.keys()之类的方法被遍历到。如果存在value,就为descriptor添加value和writable属性,如果不存在,就直接使用get和set属性。
最后,使用Object.defineProperty方法为构造函数添加属性。
function_defineProperties(target,props){ for(vari=0;iclass类实现
varPerson= /*#__PURE__*/ function(){ functionPerson(name,age){ _classCallCheck(this,Person); this.name=name; this.age=age; } _createClass(Person,[{ key:"toString", value:functiontoString(){ return'('+this.name+','+this.age+')'; } }]); returnPerson; }(); varp=newPerson('Mia',18);解析:
不使用new调用时,this指向window,所以instanceinstanceofConstructor为false,抛出异常。
通过调用_createClass函数,遍历函数数组。key为方法名,若有value说明是有具体的function声明,若无value说明使用了get或set方法。
结尾
读到这相信大家对class的实现有了更深的理解。最近笔者一边在忙毕业设计,一边整理了这道阿里前端面试题的解析,评论区欢迎对class实现这一问题进行讨论。另外,class中的extend也是很有趣的实现,在下一篇文章会对class实现继承进行解析。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。