理解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、ios、android 或 Windows 设备上进行远程测试 @ http:// modern.IE。
前端大全
--------------------------------------
商务合作QQ:2302462408
投稿网址:top.jobbole.com
以上是关于理解Ecmascript 6中的类和继承的主要内容,如果未能解决你的问题,请参考以下文章