理解ECMAScript 6:类和继承

原文地址:SitePoint
无责任翻译:Sekai


这是微软的Web开发技术系列中的一部分,谢谢那些支持让SitePoint成为可能的伙伴们。

我将跟你们分享一系列的关于ECMAScript 6,分享我的心情,并且阐述这部分内容如何为你所用。我希望观众老爷们能像我一样享受这个过程。

首先,我在微软的Project Spartan(Sekai注:就是现在的Edge浏览器)项目里编写浏览器渲染引擎,这个项目为我们所知(爱?)多年的IE引擎做出了巨大的提升。我个人最喜欢的改进就是它对ECMAScript 6有了很大的支持。对我来说,这对编写庞大的Web应用有了巨大的好处。

根据这个兼容性图表ES6 on status.modern.IE,在Project Spartan中,我们当前已经支持大约70%的ECMAScript 6的特性。
这里写图片描述

我爱Javascript,但是当使用在类似Babylon.js之类的巨大的应用中的时候,我更喜欢TypeScript,就是现在支持Angular 2的那个。原因就是JS(或者说ECMAScript 5)不包含所有的我写其他大项目的语法特性。比如我没法使用类和继承。

所以,事不宜迟,让我们直接一点:

创建一个类

Javascript是一个面向原型的语言,而且它可以使用ECMAScript 5模拟类和继承。

灵活的使用函数,我们能模拟处理类的时候的封装行为。一个技巧就是扩展一个对象的原型:

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函数),这可以使它包含实例属性。使用原型(prototype)可以让我们定义那种可以作为实例方法的函数。

这当然可以正常运行,但是你得首先了解原型继承的相关知识,而且对于一个之前使用基于类的编程语言的程序猿来说,这种方式看起来十分怪异。更奇怪的是,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的情况下调用构造器。还有一点就是方法是不可以被枚举出来的。

另一个基于类的开发特性就是getter和setter方法,它们俩同样被ES6所支持。这可以更加明显的显示出方法本身是干嘛的:

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;

很方便吧?

但是这里我们使用了一个JS中一个很常见的小问题:Animal类中有个并不是真的私有的私有属性_age。我之前对此写了这篇文章

幸好,ECMAScript 6提供了一个新特性Symbol来支持这个功能:

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对于变量名的唯一性是很有用的,但是唯一并不代表着私有。唯一只不过意味着如果你需要用到一个跟其他标识不一样的标识,就实例化一个新的Symbol吧!

但是这并不是真正意义上的私有,因为通过Object.getOwnPropertySymbols,其他人就可以访问到你的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);

太难读了,是不?

但是在ES6中看起来好多了:

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代码变得更重要了。

首先,所有最新版本的TypeScript(1.4)已经开始支持ES6代码(通过使用letconst关键字),所以你只需要保留你原本的TypeScript代码,然后通过使用这些新功能来生产ES6代码。

但是,当你仔细观察一些TypeScript代码,你会发现这就像你在看一些没有类型的ES6代码一样。所以,现在学习TypeScript就是为了在未来更好的理解ES6的途径。

结语

Sekai注:翻译到这里有点后悔了。。

使用TypeScript,你现在就可以使用这一切,并生成跨浏览器的ES5代码。如果你想在浏览器中直接使用ES6,你可以升级到Win10,并且在Project Spartan中用它的渲染引擎测试。如果你不想这么做,你也可以用一些新的浏览器的特性,你也可以在这里用Project Spartan来进入一台Win10电脑。这在你的MacOS或者Linux中也是可以运行的。

当然,Project Spartan不是唯一支持标准ES6的浏览器,其他浏览器也正在跟进,你可以在这里追踪他们的进度。

支持ES6的Javascript的未来是非常光明的,并且老实说,我都等不及看到所有现代浏览器都支持ES6了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值