前言今天学习TypeScript继承章节然后阅读了阮一峰老是的ES6入门发现十分有必要针对TypeScript类与ES6标准类进行系统的总结。TypeScript的出现就是为了贴近面向对象的语法并且TypeScript参考ES6规范通过TypeScript编译器编译成标准ES5代码。正是由于TypeScript的定位导致了很多语法与ES6语法相似但又有些许不同正是由于这种特征使人无法清楚的区分哪些是ES6已有的特性而哪些又是TypeScript所特有的。接下来本文将对TypeScript特有语法以及ES6已有语法进行归类总结希望以下内容能帮助到大家。JavaScript中何为类既然是对类的探究那么我们就必须先知道在JavaScript的世界中什么是类类又是从何而来的。首先ES5中是不存在类(class)的概念的而只存在方法(function)以下便是ES5中对于一个方法的声明functionPerson(name){this.namename;}Person.prototype.sayNamefunction(){console.log(I amthis.name);}varbobnewPerson(Bob);可见ES5中的方法是可以使用new进行实例化的正是由于ES5中写法与传统的面向对象语言比如(C 和 Java)的差异很大于是乎ES6就提供了接近传统语言的写法引入了类class的概念。但是我们要知道ES6的类(class)在很多非现代浏览器上是无法支持的于是就需要将ES6的类转化成ES5的代码以便支持传统浏览器并且使用ES5现有的特性来模拟很多类的特性比如继承、静态方法、访问限定符等。TypeScript就是一个基于ES6语法的编译器其可将以ts结尾的TypeScript代码编译成ES5代码。弄清楚了上述关系之后我们首先就来看看ES6规范中对于类是如何定义的并且支持些什么吧ES6中的类ES6中的类与传统面向对象的语法就极为相似以下就是上述Person类在ES6中的写法classPerson{constructor(name){this.namename;}sayName(){console.log(I amthis.name);}}letbobnewPerson(Bob);以上示例代码中有一个constructor方法还有一个sayName方法其中constructor方法对应于ES5中的Person构造函数而sayName对应于ES5中原型链上的sayName方法。参照阮一峰老师的ES6入门 -构造函数的prototype属性在 ES6 的“类”上面继续存在。事实上类的所有方法都定义在类的prototype属性上面ES6的类方法与ES5的类方法唯一的不同就是ES6的方法是non-enumerable(不可枚举)的而ES5由于是直接使用class.prototype.xxx function(){}因此ES5的方法是可枚举的。并且constructor中的this无论是ES5还是ES6都是指向的实例对象。TypeScript中的类见过了ES5与ES6中对于类的定义之后那么TypeScript中是如何定义类的呢请看以下代码classPerson{name:string;//需要指明属性类型constructor(name:string){//需要指明参数类型this.namename;}sayName(){console.log(I amthis.name);}}letbobnewPerson(Bob);可见TypeScript的类定义与ES6十分相似只不过TypeScript中增加了属性类型定义。以下将针对我认为ES6与TypeScript中区别最大的部分和最重要的部分进行描述如果希望知道更多细节请阅读TypeScript文档和ES6入门访问限定符在传统面向对象语言中我们最常见的访问限定符有private,public,protected但是在ES6中针对访问限定符的定义还是处于提案阶段现也没给出合适的解决方案以下内容截取自ES6入门私有方法和私有属性是只能在类的内部访问的方法和属性外部不能访问。这是常见需求有利于代码的封装但 ES6 不提供只能通过变通方法模拟实现。classWidget{// 公有方法foo(baz){this._bar(baz);}// 私有方法_bar(baz){returnthis.snafbaz;}// ...}上面代码中_bar方法前面的下划线表示这是一个只限于内部使用的私有方法。但是这种命名是不保险的在类的外部还是可以调用到这个方法。另一种方法就是索性将私有方法移出模块因为模块内部的所有方法都是对外可见的。classWidget{foo(baz){bar.call(this,baz);}// ...}functionbar(baz){returnthis.snafbaz;}上面代码中foo是公开方法内部调用了bar.call(this, baz)。这使得bar实际上成为了当前模块的私有方法。还有一种方法是利用Symbol值的唯一性将私有方法的名字命名为一个Symbol值。constbarSymbol(bar);constsnafSymbol(snaf);exportdefaultclassmyClass{// 公有方法foo(baz){this[bar](baz);}// 私有方法[bar](baz){returnthis[snaf]baz;}// ...};上面代码中bar和snaf都是Symbol值一般情况下无法获取到它们因此达到了私有方法和私有属性的效果。但是也不是绝对不行Reflect.ownKeys()依然可以拿到它们。而针对访问限定符的问题TypeScript给出了自己的解决方案 - TypeScript通过在编译阶段对访问限定符进行分析以报错的方式提示用户不合访问限定符规则的使用方式。以下为使用了访问限定符的TypeScript代码使用tsc命令打包时会报错classPerson{privatename:string;//需要指明属性的类型,并且可以使用访问限定符publicconstructor(name:string){//需要指明参数的类型并且可以使用访问限定符this.namename;}publicsayName(){console.log(I am${this.name});//可以使用ES6模板语法}}letbobnewPerson(Bob);bob.sayName();// I am Bobconsole.log(bob.name);// Error: Property name is private and only accessible within class PersonTypeScript编译报错并不会影响ES5代码的生成varPerson/** class */(function(){functionPerson(name){this.namename;//private限定符对代码没有任何影响}Person.prototype.sayNamefunction(){console.log(I am this.name);};returnPerson;}());varbobnewPerson(Bob);bob.sayName();// I am Bobconsole.log(bob.name);//Bob通过阅读上述ES5代码可以看到编译之后的结果没有对private做任何逻辑上的限制上述代码在Chrome控制台中能无Error运行。可见TypeScript对于private,public,protected的限定都处于编译阶段。继承继承是面向对象语言中最为重要的一节但是JavaScript的继承与C#和Java有很大的区别JavaScript是采用的原型继承。ES6和TypeScript也遵循这一设计规范只不过给出了语法糖来让代码更加易于理解与书写。Class 可以通过extends关键字实现继承这比 ES5 的通过修改原型链实现继承要清晰和方便很多。(但是其实现本质也是ES5的原型链继承)classManextendsPerson{constructor(name){super(name);this.genderman;}sayName(){console.log(I am a this.gender, name is this.name);}}letbobnewMan(Bob);bob.sayName();// I am a man, name is BobTypeScript的继承与ES6代码及其相似代码如下classManextendsPerson{gender:string;//需要提前声明gender属性的类型constructor(name:string){//传入的参数需要指明类型super(name);this.genderman;}sayName(){console.log(I am a${this.gender}, name is${this.name});//可以使用ES6模板语法}}letbobnewMan(Bob);bob.sayName();//I am a man, name is Bob关于继承ES6与TypeScript有以下共同点都是采用extends语法进行继承在constructor中都需要首先使用super(xxx)调用父类构造函数然后才能给this进行赋值supper作为方法时指向父类构造函数而作为对象且在普通方法中时指向父类的原型在静态方法中时指向父类。(但TypeScript中直接使用静态方法中的supper获取属性值时会报错Only public and protected methods of the base class are accessible via the super keyword.表示supper在子类静态方法中只能调用父类的静态方法可是最终编译的结果却符合ES6规范这里可能是TypeScript编译器中的BUG有兴趣的可以自己试试)最终都是通过ES5的原型链进行继承不同点TypeScript提供抽象类抽象方法和接口继承具体使用请参考TypeScript官方文档。TypeScript可以对类中属性或方法使用访问限定符。参考代码TypeScript通过以下代码来实现的继承var__extends(thisthis.__extends)||(function(){//修改子类的原型链指向父类的prototypevarextendStaticsfunction(d,b){extendStaticsObject.setPrototypeOf||({__proto__:[]}instanceofArrayfunction(d,b){d.__proto__b;})||function(d,b){for(varpinb)if(b.hasOwnProperty(p))d[p]b[p];};returnextendStatics(d,b);};returnfunction(d,b){extendStatics(d,b);function__(){this.constructord;}d.prototypebnull?Object.create(b):(__.prototypeb.prototype,new__());};})();上述示例使用tsc编译之后的代码varPerson/** class */(function(){functionPerson(name){this.namename;}Person.getOtherfunction(){returnthis.other;};Person.prototype.sayNamefunction(){console.log(I am this.name);};Person.other123;returnPerson;}());varMan/** class */(function(_super){__extends(Man,_super);functionMan(name){var_this_super.call(this,name)||this;//使用_super.call来将父类的实例属性复制到子类对象上_this.genderman;console.log(_super.prototype.sayName);//子类普通方法中使用super此时super指向父类的prototypereturn_this;}Man.supperFuncfunction(){console.log(_super.other);//子类静态方法中使用super此时super指向父类};Man.prototype.sayNamefunction(){console.log(I am a this.gender , name is this.name);};returnMan;}(Person));varbobnewMan(Bob);bob.sayName();如果对上述代码运行逻辑感兴趣可以自行研究。总结在学习TypeScript过程中由于官方文档并没有对ES6与TypeScript的内容进行区分这导致常常无法分清哪些功能是ES6已经明确定义且已纳入标准的哪些是TypeScript自定义的语法。于是我希望通过本文章来对TypeScript的类与ES6的类的区别进行记录防止混淆ES6与TypeScript。本文参考文档ECMAScript 6 入门TypeScript官方文档本文行文仓促如有错误还望斧正 ?