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


      本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出 原文連接,博客地址為 http://www.cnblogs.com/jasonnode/ 。該系列課程是匯智網 整理編寫的,課程地址為 http://www.dwz.cn/3e6Yml

 


 

Class

  1. Class基本語法
  2. Class的繼承
  3. class的取值函數(getter)和存值函數(setter)
  4. Class的Generator方法
  5. Class的靜態方法
  6. new.target屬性
  7. 修飾器

Module

  1. export命令
  2. import命令
  3. 模塊的整體輸入
  4. module命令
  5. export default命令
  6. 模塊的繼承

Class基本語法


ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,作為對象的模板。通過class關鍵字,可以定義類。

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

上面代碼定義了一個“類”,可以看到里面有一個constructor方法,這就是構造方法,而this關鍵字則代表實例對象。

constructor方法

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

實例對象

var point = new Point(2, 3);

name屬性

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

class表達式

與函數一樣,Class也可以使用表達式的形式定義。

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

Class的繼承


Class之間可以通過extends關鍵字,實現繼承。

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

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方法。

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

let cp = new ColorPoint(25, 8, 'green');
 
cp instanceof ColorPoint // true
cp 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.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
 
export {firstName, lastName, year};

import命令


使用export命令定義了模塊的對外接口以后,其他JS文件就可以通過import命令加載這個模塊(文件)。

// main.js
import {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.js
export function area(radius) {
  return Math.PI * radius * radius;
}
export function circumference(radius) {
  return 2 * Math.PI * radius;
}

然后,main.js輸入circlek.js模塊。

// main.js
import { 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.js
module circle from 'circle';
 
document.write("圓面積:" + circle.area(4));
document.write("圓周長:" + circle.circumference(14));

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

export default命令


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

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

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

其他模塊加載該模塊時,import命令可以為該匿名函數指定任意名字。

// import-default.js
import 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方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM