ECMAScript 6新特性印象之二:面對對象和模塊化

本文參考瞭如下文章/PPT:javascript

以前的文章:html

面對對象

1.關鍵字 Classjava

class Artist {
    constructor(name) {
        this.name = name;
    }
    perform() {
        return this.name + " performs ";
    }
}

class Singer extends Artist {
    constructor(name, song) {
        super.constructor(name);
        this.song = song;
    }
    perform() {
        return super.perform() + "[" + this.song + "]";
    }
}

let james = new Singer("Etta James", "At last");
james instanceof Artist; // true
james instanceof Singer; // true

james.perform(); // "Etta James performs [At last]"

看上面例子就能明白。注意幾個關鍵字extendssupergit

雖然ES6的Class本質上仍是語法糖,但這麼設計有它的目的。es6

在ES5中, function關鍵字承擔着三個職責:github

  1. 定義函數。
  2. 定義方法屬性。
  3. 定義類的constructor,配合new建立新對象。

在ES6中,第2點被屬性方法定義(Method definitions)替代,第3點被Class關鍵字替代。一個關鍵字只承擔一個職責,再也不是滿屏function,足夠清晰了吧?編程

有幾點要注意的:segmentfault

  1. 類的body只能包含屬性方法,不能包含屬性值。屬性值放到constructor方法裏。
  2. 屬性方法能夠是生成器,在方法名前家*就能夠。
  3. 聲明類(Class Declaration)並不會被提高(hoisted)。
  4. 若是沒有指定constructor,那會有個默認調用super的。

2.繼承 Extendingpromise

繼承的幾種狀況和規則:瀏覽器

  1. 不要繼承空類, class Foo {},由於:

    • Foo的原型(prototype)是Function.prototype(全部函數的原型都是這個)。
    • 而Foo.prototype的原型是Object.prototype
    • 這種繼承就和函數同樣了。
  2. 繼承nullclass Foo extends null {}

    • Foo的原型是Function.prototype
    • Foo.prototype的原型是null
    • 這樣Object.prototype的屬性方法都不會繼承到Foo中。
  3. 繼承構造器:class Foo extends SomeClass

    • Foo的原型是SomeClass
    • Foo.prototype的SomeClass.prototype
    • 這樣,類方法屬性也會被繼承。
  4. 繼承非構造器(對象):class Foo extends SomeObject

    • Foo的原型是Function.prototype
    • Foo.prototype的SomeClass
  5. 錯誤檢查:繼承的目標必定要是個對象或者null。若是是繼承構造器,那麼構造器的原型必定要是個對象或者null。

  6. 類聲明其實建立的是可變let綁定(binding,函數式編程會比較熟悉這個概念)。對於一個類Foo:

    • Foo的原型是不可改寫,且不可枚舉的。
    • Foo的構造器是可改寫,但不可枚舉。
    • Foo的原型函數(Foo.prototype.*)是可改寫,但不可枚舉。

模塊

ES6的內置模塊系統借鑑了CommonJS和AMD各自的優勢:

  • 具備CommonJS的精簡語法、惟一導出出口(single exports)和循環依賴(cyclic dependencies)的特色。
  • 相似AMD,支持異步加載和可配置的模塊加載。

不單單借鑑,ES6還對這些功能進行了優化:

  • 語法比CommonJS更精簡。
  • 支持結構的靜態分析(靜態檢查,優化等等)。
  • 循環依賴的支持比CommonJS更好。

1.語法

ES6模塊的導出出口形式有兩種:命名導出(一個模塊多個導出)、標準導出(一個模塊一個導出)。

命名導出 Named exports

//-------lib.js----------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//-------main1.js--------
import { sqaure, diag } from 'lib';

console.log(square(11)); // 121
console.log(diag(3,4)); // 5

//或者這樣,命名導入的名稱:
//-------main2.js--------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(3,4)); // 5

標準導出 Default exports

//-------MyClass.js-----
// 注意,`export`的操做對象是表達式,通常是沒有名字的。
export default class { ... };

//-------main3.js--------
// 經過模塊名字識別
import MyClass from 'MyClass';
let inst = new MyClass();

固然,這兩種導出也能夠混合使用。本質上,標準導出只是指名導出的一種,名稱是「default」而已。

就目前瞭解的來看,ES6的模塊導出貌似有些繁瑣,還不如CommonJS直接經過object導出利索。

2.設計初衷 Design goals

TC39在討論ES6的模塊化問題時,主要考慮了下面幾點:

  1. 直接導出優先
  2. 靜態模塊結構
  3. 同步/異步載入都要支持
  4. 支持循環依賴

第一點就是要簡單已用。而靜態模塊結構更可能是出於性能優化、靜態類型檢查(沒錯,就是這個,動態語言爲何要有這個,其實仍是爲了性能)和之後的可能會加入的「宏模板」功能。

3.更多導入/導出寫法舉例

導入:

// 標準導入,命名導入
import theDefault, { named1, named2 } from 'src/mylib';
import theDefault from from 'src/mylib';
import { named1, named2 } from 'src/mylib';

// 重命名
import { named1 as myNamed1, named2 } from 'src/mylib';

// 將導入的模塊定義爲一個對象
// 模塊的每一個屬性都是該對象的同名方法屬性
import * as mylib from 'src/mylib';

// 僅讀取模塊,不導入任何功能
import 'src/mylib';

導出:

// 使用關鍵字**export**導出
export let myVar1 = ...;
export function MyFunc() {...}
export function* myGeneratorFunc() {...}

// 也能夠以對象形式導出
const MY_CONST = ...;
function myFunc() { ... }

export { MY_CONST, myFunc }
// 固然,名字不必定要相同
export { MY_CONST as THE_CONST, myFunc as theFunc };

// 支持重導出,即在當前模塊導出其餘模塊的功能 方便hack
export * from 'src/other_module';
export { foo, bar } from 'src/other_module';

上面說的這些語法,普通的<script>標籤是不支持的。ES6引入了一個<module>標籤,負責載入模塊。<script>標籤只能經過下面將要介紹的模塊API進行模塊載入。

4.模塊元數據

不單單是導入別的數據,ES6還能經過導入當前模塊拿到當前模塊的信息:

import { url } from this module;
console.log(url);

這就像Ruby裏的____FILENAME____

5.模塊載入接口 Module loader API

API天然是爲了經過代碼控制模塊載入的,算是多少彌補了靜態結構靈活上上的缺陷。

每一個瀏覽器平臺都會有一個名爲System的全局變量,經過這個變量調用相應接口,異步載入模塊(結合ES6的promises):

System.import('some_module')
.then(some_module => {
    ...
})
.catch(error => {
    ...
})

//固然也能夠一次載入多個模塊
Promise.all(
    ['module1', 'module2', 'module3']
).map(x => Symtem.import(x)))
.then(function([module1, module2, module3]) {
    ...
});

其餘接口:

  • System.module(source, options?)將source中的代碼求值成一個模塊。
  • System.set(name, module)註冊一個經過System.module生成的模塊。
  • System.define(name, source, options?)前兩個接口的結合。

更多關於模塊的說明

請看看Yehuda Katz的這一篇:Javascript Modules

相關文章
相關標籤/搜索