理解Ecmascript 6中的类和继承

Posted 前端大全

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Ecmascript 6中的类和继承相关的知识,希望对你有一定的参考价值。


英文:David Catuhe

译者:伯乐在线 - 刘健超-J.c

网址:http://web.jobbole.com/83372/


我想分享一系列关于 ECMAScript 6 的文章,分享我对它的热爱并解释如何让它为你所用。希望你在阅读的时候能和我写作的时候一样享受快乐。




首先,我是在微软负责 Spartan 浏览器 渲染引擎项目,该引擎是在我们多年来熟悉的(并爱上的?)Internet Explorer 引擎的基础上,做出巨大改进。我最喜欢该引擎的特性是它支持大部分 ECMAScript 6。对我而言,这对编写大型 web 应用程序会带来巨大好处。


到目前为止,已有将近 7 成 ECMAScript 6 特性在 Spartan 项目上得到支持,通过 http://kangax.github.io/compat-table/es6/ 和 ES6 on status.modern.IE 可查看相关信息。



我喜欢 javascript,但运用在大型项目上,如 Babylon.js,我更情愿用 TypeScript。Angular 2 现在正是基于它实现的。没用 JavaScript 的原因是它没有我习惯使用其它语言编写大型程序的所有语法特性,比如类和继承。


所以事不宜迟,让我们开始吧。


创建类


JavaScript 是一种面向原型的语言,并在 ECMAScript 5 中,能模拟类和继承。


在 JavaScript 中,灵活的函数允许我们模拟常用于处理类的封装。通过这个技巧,能扩展对象的原型。


var Animal = (function () {

function Animal(name) {

this.name = name;

}

// Methods

Animal.prototype.doSomething = function () {

console.log("I'm a " + this.name);

};

return Animal;

})();


var lion = new Animal("Lion");

lion.doSomething();


在上面的代码中,定义了一个带有“属性”和“方法”的“类”。


这个构造器通过函数(函数 Animal)定义,并能实例化属性。通过使用原型,可定义能被实例化的函数。


而问题是,假设你了解原型继承和一些基于类继承的语言,你会感到很混乱。奇怪的是,JavaScript 有 class 关键字,却不能做任何事。而现在 ECMAScript 6 用上它了,这使我们可以写更短的代码:


class AnimalES6 {

constructor(name) {

this.name = name;

}


doSomething() {

console.log("I'm a " + this.name);

}

}


var lionES6 = new AnimalES6("Lion");

lionES6.doSomething();


运行结果和前面的一样,但对于习惯于写类的开发者来说,更易编写和阅读。因为这不需要原型,并且能使用 “constructor” 关键字定义构造器。


此外,类引入了一些 ECMAScript 5 新的语法。例如,必须通过 new 调用构造函数,或者不能用 new 尝试去构造类方法。另一个改变是,方法都是不可枚举的。


有趣的地方是:两个版本可同时共存使用。


归根结底,即使用 new 关键字实例化一个添加在原型的函数。“方法”在这里仅仅是对象的一个函数属性。


另一个基于类开发的核心特性是,getter 和 setter 都在 ECMAScript 6 得到支持。这使方法的用途变得更加明显:


class AnimalES6 {

constructor(name) {

this.name = name;

this._age = 0;

}


get age() {

return this._age;

}


set age(value) {

if (value < 0) {

console.log("We do not support undead animals");

}


this._age = value;

}


doSomething() {

console.log("I'm a " + this.name);

}

}


var lionES6 = new AnimalES6("Lion");

lionES6.doSomething();

lionES6.age = 5;


十分方便,对吧?


但这会看到一个 JavaScript 的常规警告:“不是真正私有的”私有成员(_age)。我之前在这个主题 里写了一篇关于这个问题的文章。


辛亏,现在有一个更好的、新的 ECMAScript 6 特性来完成这个事情:symbols (符号):


var ageSymbol = Symbol();


class AnimalES6 {

constructor(name) {

this.name = name;

this[ageSymbol] = 0;

}


get age() {

return this[ageSymbol];

}


set age(value) {

if (value < 0) {

console.log("We do not support undead animals");

}


this[ageSymbol] = value;

}


doSomething() {

console.log("I'm a " + this.name);

}

}


var lionES6 = new AnimalES6("Lion");

lionES6.doSomething();

lionES6.age = 5;


那么,什么是 symbol (符号)呢?它是唯一且不可改变的数据类型,可用于标识对象属性。如果没有 symbol (符号),是不能获取该属性的。


所以,这是一个可创建更“私有”的成员属性途径。


或者,至少没那么容易访问到。Symbol (符号)对于创建唯一的名字是很有用的,但唯一性不意味着私有性。唯一性只意味着,为一把钥匙添加一个新符号后,当你需要它的时候,肯定不会被其它钥匙所干扰。


但这还不是正真的私有,因为通过 Object.getOwnPropertySymbols,downstream consumers 能获取 symbol (符号)属性。


处理继承


一旦拥有类,也会想拥有继承。这里再次利用 ES5 模拟继承,但实现起来比较复杂。


例如,这里利用 TypeScript 模拟继承:


var __extends = this.__extends || function (d, b) {

for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];

function __() { this.constructor = d; }

__.prototype = b.prototype;

d.prototype = new __();

};

var SwitchBooleanAction = (function (_super) {

__extends(SwitchBooleanAction, _super);

function SwitchBooleanAction(triggerOptions, target, propertyPath, condition) {

_super.call(this, triggerOptions, condition);

this.propertyPath = propertyPath;

this._target = target;

}

SwitchBooleanAction.prototype.execute = function () {

this._target[this._property] = !this._target[this._property];

};

return SwitchBooleanAction;

})(BABYLON.Action);


读起来真的不容易。


但用 ECMAScript 6 能更好地实现:


var legsCountSymbol = Symbol();

class InsectES6 extends AnimalES6 {

constructor(name) {

super(name);

this[legsCountSymbol] = 0;

}


get legsCount() {

return this[legsCountSymbol];

}


set legsCount(value) {

if (value < 0) {

console.log("We do not support nether or interstellar insects");

}


this[legsCountSymbol] = value;

}


doSomething() {

super.doSomething();

console.log("And I have " + this[legsCountSymbol] + " legs!");

}

}


var spiderES6 = new InsectES6("Spider");

spiderES6.legsCount = 8;

spiderES6.doSomething();


多亏 “extends” 关键字,它能让你继承类而产生子类,并能使用 “super” 关键字保持对根类的引用。


在这些新增且强大的特性下,无需再通过原型来创建类和实现继承了。


为什么现在使用 TypeScript 比之前更具实质性…


在浏览器支持所有这些新特性之前,我认为使用 TypeScript 去产生 JavaScript 代码更靠谱。


首先,所有 1.4 版本以上的 TypeScript 支持 ECMAScript 6 代码(有 let 和 const 关键字)。所以只要保留 TypeScript 代码,就能使用这些新选项去产生 ECMAScript 6 代码。


但如果你仔细观察一些 TypeScript 代码,会发现这些代码看起来像 ECMAScript 6 。所以现在开始学习 TypeScript的话 ,以后就能很好地理解 ECMAScript 6 了!


总结


使用 TypeScript ,代码会被转换成能跨浏览器运行的 ECMAScript 5 。如果想直接在浏览器上使用 ECMAScript 6 ,可升级到 Windows 10并在 Spartan 项目的渲染引擎进行测试。如果不想这么麻烦,只想测试一些新浏览器特性,那么也可以在 Windows 10的电脑上,用 Spartan 项目打开 http:// remote.modern.ie 。这也可以在 MacOS 或 Linux 机器上运行。


当然, Spartan 项目并不是唯一支持公开标准的 ES6 。其它浏览器也已经支持了,你可在 http://kangax.github.io/compat-table/es6/ 查看支持程度。


ECMAScript 6 的前途是光明的,老实说我已情不自禁地想看到,它在所有现代浏览器上得到广泛支持。


这篇文章是 Microsoft web 开发教程系列的一部分。我们激动地向你们分享 Spartan 项目 和 它新渲染引擎 。获取免费虚拟机器或在 Mac、iosandroid 或 Windows 设备上进行远程测试 @ http:// modern.IE。



前端大全

--------------------------------------

商务合作QQ:2302462408

投稿网址:top.jobbole.com

以上是关于理解Ecmascript 6中的类和继承的主要内容,如果未能解决你的问题,请参考以下文章

Java中的继承封装多态的理解

python的类和对象中的super函数的问题

JavaScript 引用类型

ECMAScript 6 引入的 JavaScript 类(class)--类同java class

Swift 中的类和协议继承

ES6中的类