ECMAScript 6教程 (三) Class和Module(類和模塊)

      本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接,博客地址爲 http://www.cnblogs.com/jasonnode/ 。該系列課程是匯智網 整理編寫的,課程地址爲 http://www.hubwiz.com/course/5594e91ac086935f4a6fb8ef/node

 

Class基本語法


ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,做爲對象的模板。經過class關鍵字,能夠定義類。函數

//定義類class Point {
    constructor(x, y) {        this.x = x;        this.y = y;
    }
    toString() {         return '('+this.x+', '+this.y+')';
    }
}

上面代碼定義了一個「類」,能夠看到裏面有一個constructor方法,這就是構造方法,而this關鍵字則表明實例對象。this

constructor方法

constructor方法是類的默認方法,經過new命令生成對象實例時,自動調用該方法。spa

實例對象

var point = new Point(2, 3);

name屬性

class Point {}
Point.name // "Point"

class表達式

與函數同樣,Class也能夠使用表達式的形式定義。prototype

const MyClass = class Me {
    getClassName() {        return Me.name;
    }
};

Class的繼承


Class之間能夠經過extends關鍵字,實現繼承。code

子類會繼承父類的屬性和方法。orm

class Point {
    constructor(x, y) {        this.x = x;        this.y = y;
    }
}
 
class ColorPoint extends Point {
    constructor(x, y, color) {        this.color = color; // ReferenceError        super(x, y);        this.color = color; // 正確    }
}

 

上面代碼中,子類的constructor方法沒有調用super以前,就使用this關鍵字,結果報錯,而放在super方法以後就是正確的。對象

注意:ColorPoint繼承了父類Point,可是它的構造函數必須調用super方法。blog

下面是生成子類實例的代碼。繼承

let cp = new ColorPoint(25, 8, 'green');
 
cp instanceof ColorPoint // truecp instanceof Point // true

class的取值函數(getter)和存值函數(setter)


在Class內部能夠使用get和set關鍵字,對某個屬性設置存值函數和取值函數。

class MyClass {
  get prop() {
    return 'getter';
  }
  set prop(value) {
    document.write('setter: '+value);
  }
}
 
let inst = new MyClass();
 
inst.prop = 123;// setter: 123 inst.prop// 'getter'

Class的Generator方法


若是某個方法以前加上星號(*),就表示該方法是一個Generator函數。

class Foo {
    constructor(...args) {        this.args = args;
    }    * [Symbol.iterator]() {        for (let arg of this.args) {
            yield arg;
        }
    }
} 
for (let x of new Foo('hello', 'world')) {
    document.write(x);
}// hello// world

上面代碼中,Foo類的Symbol.iterator方法前有一個星號,表示該方法是一個Generator函數。Symbol.iterator方法返回一個Foo類的默認遍歷器,for...of循環會自動調用這個遍歷器。

上面代碼中,prop屬性有對應的存值函數和取值函數,所以賦值和讀取行爲都被自定義了。

Class的靜態方法


類至關於實例的原型,全部在類中定義的方法,都會被實例繼承。若是在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,這就稱爲「靜態方法」。

class Foo {
    static classMethod() {        return 'hello';
    }
}
Foo.classMethod() // 'hello'var foo = new Foo();
foo.classMethod()// TypeError: undefined is not a function

上面代碼中,Foo類的classMethod方法前有static關鍵字,代表該方法是一個靜態方法,能夠直接在Foo類上調用(Foo.classMethod()),而不是在Foo類的實例上調用。若是在實例上調用靜態方法,會拋出一個錯誤,表示不存在該方法。

父類的靜態方法,能夠被子類繼承。

class Foo {
    static classMethod() {        return 'hello';
    }
}
class Bar extends Foo {
}
Bar.classMethod(); // 'hello'

上面代碼中,父類Foo有一個靜態方法,子類Bar能夠調用這個方法。

靜態方法也是能夠從super對象上調用的。

class Foo {
    static classMethod() {        return 'hello';
    }
}
class Bar extends Foo {
    static classMethod() {        return super.classMethod() + ', too';
    }
}
Bar.classMethod();

new.target屬性


new是從構造函數生成實例的命令。ES6爲new命令引入了一個new.target屬性,(在構造函數中)返回new命令做用於的那個構造函數。若是構造函數不是經過new命令調用的,new.target會返回undefined,所以這個屬性能夠用來肯定構造函數是怎麼調用的。

function Person(name) {
  if (new.target !== undefined) {
    this.name = name;
  } else {
    throw new Error('必須使用new生成實例');
  }
} 
// 另外一種寫法function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必須使用new生成實例');
  }
} 
var person = new Person('張三'); // 正確var notAPerson = Person.call(person, '張三'); // 報錯

 

上面代碼確保構造函數只能經過new命令調用。

  • Class內部調用new.target,返回當前Class。

  • 子類繼承父類時,new.target會返回子類。

修飾器


修飾器(Decorator)是一個表達式,用來修改類的行爲。

修飾器函數能夠接受三個參數,依次是目標函數、屬性名和該屬性的描述對象。後兩個參數可省略。上面代碼中,testable函數的參數target,就是所要修飾的對象。若是但願修飾器的行爲,可以根據目標對象的不一樣而不一樣,就要在外面再封裝一層函數。

function testable(isTestable) {return function(target) {
  target.isTestable = isTestable;
}
}
 
@testable(true) class MyTestableClass () {}
document.write(MyTestableClass.isTestable) // true @testable(false) class MyClass () {}
document.write(MyClass.isTestable) // false

若是想要爲類的實例添加方法,能夠在修飾器函數中,爲目標類的prototype屬性添加方法。

function testable(target) {
    target.prototype.isTestable = true;
}
 
@testable
class MyTestableClass () {}
 
let obj = new MyClass();
 
document.write(obj.isTestable) // true

export命令


模塊功能主要由兩個命令構成:export和import。

  • export命令用於用戶自定義模塊,規定對外接口;

  • import命令用於輸入其餘模塊提供的功能,同時創造命名空間(namespace),防止函數名衝突。

ES6容許將獨立的JS文件做爲模塊,容許一個JavaScript腳本文件調用另外一個腳本文件。

現有profile.js文件,保存了用戶信息。ES6將其視爲一個模塊,裏面用export命令對外部輸出了三個變量。

// profile.jsvar firstName = 'Michael';var lastName = 'Jackson';var year = 1958;
 
export {firstName, lastName, year};

import命令


使用export命令定義了模塊的對外接口之後,其餘JS文件就能夠經過import命令加載這個模塊(文件)。

// main.jsimport {firstName, lastName, year} from './profile'; 
function sfirsetHeader(element) {
  element.textContent = firstName + ' ' + lastName;
}

上面代碼屬於另外一個文件main.js,import命令就用於加載profile.js文件,並從中輸入變量。import命令接受一個對象(用大括號表示),裏面指定要從其餘模塊導入的變量名。大括號裏面的變量名,必須與被導入模塊(profile.js)對外接口的名稱相同。

若是想爲輸入的變量從新取一個名字,import語句中要使用as關鍵字,將輸入的變量重命名。

import { lastName as surname } from './profile';

ES6支持多重加載,即所加載的模塊中又加載其餘模塊。

模塊的總體輸入


export命令除了輸出變量,還能夠輸出方法或類(class)。下面是一個circle.js文件,它輸出兩個方法area和circumference。

// circle.jsexport function area(radius) {
  return Math.PI * radius * radius;
}
export function circumference(radius) {
  return 2 * Math.PI * radius;
}

而後,main.js輸入circlek.js模塊。

// main.jsimport { area, circumference } from 'circle';
document.write("圓面積:" + area(4));
document.write("圓周長:" + circumference(14));

上面寫法是逐一指定要輸入的方法。另外一種寫法是總體輸入。

import * as circle from 'circle';
document.write("圓面積:" + circle.area(4));
document.write("圓周長:" + circle.circumference(14));

module命令


module命令能夠取代import語句,達到總體輸入模塊的做用。

// main.jsmodule circle from 'circle';
 
document.write("圓面積:" + circle.area(4));
document.write("圓周長:" + circle.circumference(14));

module命令後面跟一個變量,表示輸入的模塊定義在該變量上。

export default命令


爲加載模塊指定默認輸出,使用export default命令。

// export-default.jsexport default function () {
  document.write('foo');
}

上面代碼是一個模塊文件export-default.js,它的默認輸出是一個函數。

其餘模塊加載該模塊時,import命令能夠爲該匿名函數指定任意名字。

// import-default.jsimport customName from './export-default';
customName(); // 'foo'

上面代碼的import命令,能夠用任意名稱指向export-default.js輸出的方法。須要注意的是,這時import命令後面,不使用大括號。

模塊的繼承


模塊之間也能夠繼承。

假設有一個circleplus模塊,繼承了circle模塊。

// circleplus.js export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
  return Math.exp(x);
}

上面代碼中的「export *」,表示輸出circle模塊的全部屬性和方法,export default命令定義模塊的默認方法。

這時,也能夠將circle的屬性或方法,更名後再輸出。

// circleplus.js export { area as circleArea } from 'circle';

上面代碼表示,只輸出circle模塊的area方法,且將其更名爲circleArea。

加載上面模塊的寫法以下。

// main.js module math from "circleplus";
import exp from "circleplus";
document.write(exp(math.pi));

上面代碼中的"import exp"表示,將circleplus模塊的默認方法加載爲exp方法。

相關文章
相關標籤/搜索