ECMAScript 2015 –類別

by vivid 11. 一月 2017 17:44

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號: N170117901
出刊日期: 2017/01/11

本文介紹ECMAScript 2015新增的Class(類別)語法。

 

Class(類別)

ECMAScript 5並不支援物件導向語言的類別(class)語法,讓不少熟悉物件導向的程式設計師感到困惑,總是得利用紆迴的方式來做出物件導向的功能。終於,ECMAScript 2015現在支援類別(class)的語法,不過它的類別和其它物件導向程式語言的類別有一些不同,新增的class語法是以prototype繼承為基礎,讓定義物件的語法較像傳統的物件導向語言,但實際上類別的型別還是function。

 

類別宣告(Class Declaration)

在ECMAScript 2015中使用class關鍵字宣告類別,隨後是類別的名稱,這種宣告方式稱「類別宣告(Class Declaration)」。類別的名稱不可以是「eval」或「arguments」,否則會出現語法錯誤訊息。同時,類別的名稱也不可以重複定義,一樣會出現語法錯誤訊息。

參考以下範例程式碼,定義一個Employee類別,在類別之中,使用constructor關鍵字定義一個建構函式,用來初始化內部成員。在建構函式之中,使用this關鍵字宣告兩個屬性:「id」與「name」,而printInfo()方法將會定義在Employee 的prototype屬性之中(屬性需要在建構函式之中宣告):

class Employee {
  constructor( id, name ) {
    this.id = id;
    this.name = name;
  }
  printInfo() {
    console.log( this.id + " , " + this.name );
  }
}

let emp1 = new Employee( 1, "Mary" );
console.log( emp1.id ); //1
console.log( emp1.name ); //Mary

emp1.printInfo(); //1 , Mary

console.log( typeof Employee ); //function
console.log( Employee === Employee.prototype.constructor ); //true

 

不過實際上Employee 類別的型別是function,你可以使用typeof關鍵字來觀察它們,換句話說,class只是提供另一個簡易的語法,將function物件包裝起來,實際上它還是function物件,因此使用「typeof Employee」程式判斷Employee型別得到的結果會是「function」。使用constructor關鍵字定義的方法是一個特殊的方法,它定義了代表類別的function,因此「Employee === Employee.prototype.constructor」這行程式比較的結果會是「true」。

所有在class大括號之中的程式,預設會自動執行在嚴格模式(strict mode)。類別不能直接當function來呼叫,一定要搭配new關鍵字使用,否則便會產生錯誤。此外,建構函式只能有一個,若重複定義也會發生錯誤。

類別成員之間,不需要使用任何符號區隔之,不過若成員定義之間包含「;」號,JavaScript引擎不會抱怨出錯,但是會自動將其忽略,參考以下範例程式碼,constructor與printInfo()之間有一個多餘的分號:

class Employee {
  constructor(id, name) {
    this.id = id;
    this.name = name;
  }
  ; //忽略
  printInfo() {
    console.log(this.id + " , " + this.name);
  }
}

 

但若包含其它符號,則會出現錯誤,例如以下範例程式碼,constructor與printInfo()之間有一個多餘的逗號:

class Employee {
   constructor(id, name) {
     this.id = id;
     this.name = name;
   }
   , //錯誤
   printInfo() {
     console.log(this.id + " , " + this.name);
   }
}

類別不像ECMAScript 5函式宣告(Function Declaration)有Hoisting特性,先定義函式宣告再使用new關鍵字建物件,或先使用new關鍵字建立物件再定義函式宣告,兩種方式都可以順利執行,而類別則無此項特性,必需須先宣告類別,再使用new關鍵字來建立物件,若程式順序有問題時,會出現錯誤,參考以下範例程式碼:

let emp1 = new Employee(1, "Mary"); //Employee is not defined

class Employee {
  constructor(id, name) {
    this.id = id;
    this.name = name;
  }
  printInfo() {
    console.log(this.id + " , " + this.name);
  }
}


 

類別運算式(Class Expressions)

除了類別宣告(Class Declaration)語法之外,類別也支援類別運算式(Class Expressions)語法。使用class關鍵字,隨後直接包含 { } 運算式,不需要類別名稱。參考以下範例程式碼,使用類別運算式改寫上述類別宣告範例 :

let Employee = class {
  constructor(id, name) {
    this.id = id;
    this.name = name;
  }
  printInfo() {
    console.log(this.id + " , " + this.name);
  }
}

let emp1 = new Employee(1, "Mary");
console.log(emp1.id); //1
console.log(emp1.name); //Mary
emp1.printInfo(); //1 , Mary
console.log(typeof Employee); //function


 

你也可以使用具名類別運算式(Named Class Expression)來定義類別,使用class關鍵字,隨後設定一個文字名稱,在下面的範例中,類別運算式的名稱(name)為Emp,在類別程式碼之中,使用name屬性(Emp.name)可以得到名稱(Emp):

let Employee = class Emp {
  constructor( id, name ) {
    this.id = id;
    this.name = name;
  }
  printInfo() {
    console.log( this.id + " , " + this.name + " , " + Emp.name );
  }
}
let emp1 = new Employee( 1, "Mary" );
console.log( emp1.id ); // 1
console.log( emp1.name ); // Mary

emp1.printInfo(); // 1 , Mary , Emp

console.log( typeof Employee ); // function
console.log( typeof Emp ); // undefined
console.log( Employee.name ); // Emp

 

Emp這個識別字只有在類別的定義之中有效,在類別之外便不存在,因此在類別之外我們使用「typeof Emp」會得到「undefined」。

 

類別視為一個值

在JavaScript中,類別可以被視為一個值,將之傳入一個function當參數,或當作function的回傳值。參考以下範例程式碼,呼叫時getEmployee()方法傳入Employee類別的定義:

class Employee {
  constructor( id, name ) {
    this.id = id;
    this.name = name;
  }
  printInfo() {
    console.log( this.id + " , " + this.name );
  }
}


function getEmployee( Employee ) {
  let emp = new Employee( 1, "Mary" );
  emp.printInfo(); // 1 , Mary
}

getEmployee( Employee );

 

 

我們可以再改寫程式碼,直接將Employee類別宣告傳入getEmployee()方法,參考以下範例程式碼,將得到一樣的執行結果:

function getEmployee( Employee ) {
  let emp = new Employee( 1, "Mary" );
  emp.printInfo(); // 1 , Mary
}

getEmployee( class Employee {
  constructor( id, name ) {
    this.id = id;
    this.name = name;
  }
  printInfo() {
    console.log( this.id + " , " + this.name );
  }
});

 

宣告順帶呼叫建構函式

類別運算式也允許在一行程式碼宣告時順帶呼叫建構函式,只要宣告時,使用new關鍵字,後面接類別運算式「class {}」,然後馬上使用小括號「( )」呼叫建構函式,並傳入參數值,參考以下範例程式碼:

 

let Employee = new class  {
  constructor( id, name ) {
    this.id = id;
    this.name = name;
  }
  printInfo() {
    console.log( this.id + " , " + this.name );
  }
}( 1, "Mary" );


Employee.printInfo(); // 1 , Mary



ECMAScript 2015也支援具名類別運算式的寫法,參考以下範例程式碼,使用new關鍵字,後面接class關鍵字與一個文字名稱「Employee」,在類別之中可以使用「Employee.name」來取得名稱「Employee」:

let Employee = new class Employee {
  constructor( id, name ) {
    this.id = id;
    this.name = name;
  }
  printInfo() {
    console.log( this.id + " , " + this.name );
    console.log( Employee.name ); // Employee
  }
}( 1, "Mary" );

Employee.printInfo(); // 1 , Mary
console.log( Employee.name ); // Mary


在類別之外就無法使用「Employee.name」來取得具名類別運算式的名稱,會得到undefined,上述範例最後一行讀取「Employee.name」讀到的是和Employee類別同名的name屬性。

存取子屬性(Accessor Property)

類別(Class)之中可以宣告存取子屬性(Accessor Property),包含getter與setter,它們實際上是方法,將會定義在prototype物件之中。若要定義getter讀取屬性值,只要使用get關鍵字,後面接屬性名稱,與小括號()。若要定義setter修改屬性值,只要使用set關鍵字,後面接屬性名稱,與小括號(),小括號之中傳入value,參考以下範例程式碼:

class Employee {
  constructor( id, name ) {
    this.id = id;
    this.name = name;
  }
  get empID() {
    return this.id;
  }
  set empID( value ) {
    this.id = value;
  }
  get empName() {
    return this.name;
  }
  set empID( value ) {
    this.name = value;
  }
  printInfo() {
    console.log( this.empID + " , " + this.empName );
  }
}

let emp1 = new Employee( 1, "Mary" );
console.log( emp1.empID ); // 1
console.log( emp1.empName ); // Mary
emp1.printInfo(); // 1 , Mary

 

以此範例而言,存取子屬性(Accessor Properties)將會定義在Employee.prototype之中。

 

使用變數定義類別成員

類別成員的名稱,如方法(Method)或存取子屬性(Accessor property),也可以使用變數來定義,只要使用 [ ] 號,在其中傳入變數即可,參考以下範例程式碼,使用x變數來為Employee類別宣告方法,接著你可以直接使用變數值(printInfo)來叫用方法,或者使用[ ] 號,在 [ ] 括號中傳入x變數來呼叫方法:

let x = "printInfo";

class Employee {
  constructor(id, name) {
    this.id = id;
    this.name = name;
  }
  get empID() {
    return this.id;
  }
  set empID(value) {
    this.id = value;
  }
  get empName() {
    return this.name;
  }
  set empID(value) {
    this.name = value;
  }
  [x]() {
    console.log(this.empID + " , " + this.empName);
  }
}

let emp1 = new Employee(1, "Mary");
console.log( emp1.empID ); // 1
console.log( emp1.empName ); // Mary
emp1.printInfo(); // 1 , Mary
emp1[x](); // 1 , Mary

 

使用變數定義存取子屬性(Accessor Property)

同樣地,類別的存取子屬性(Accessor Property)之名稱也可以使用 [變數] 語法來定義,參考以下範例程式碼,使用變數「x」的值來為Employee類別定義「empID」存取子屬性;以及使用變數「y」的值來為Employee類別定義「empName」存取子屬性 :

let x = "empID";
let y = "empName";

class Employee {
  constructor(id, name) {
    this.id = id;
    this.name = name;
  }
  get[x]() {
    return this.id;
  }
  set[x](value) {
    this.id = value;
  }
  get[y]() {
    return this.name;
  }
  set[y](value) {
    this.name = value;
  }
  printInfo() {
    console.log(this.empID + " , " + this.empName);  //1 , Mary
    console.log(this[x] + " , " + this[y]);  //1 , Mary
  }
}

let emp1 = new Employee(1, "Mary");
console.log(emp1.empID); // 1
console.log(emp1.empName); // Mary
emp1.printInfo();
console.log(emp1[x]); // 1
console.log(emp1[y]); // Mary

 

 

在類別之中的程式碼,可以使用this[x]、this[y] 語法來存取empID、empName屬性,而在類別之外,可以使用物件變數名稱emp1[x]與emp1[y]來存取屬性。

 

Generator方法

Generator是一個函式(function),回傳一個Iterator,Generator function的名稱以「*」(asterisk)號開始,再加上文字的名稱。在類別之中,一樣可以宣告Generator方法(Generator Method)來取得Iterator,只要在方法前方,加上「*」號,來表示它是一個Generator方法,在方法中便可使用yield關鍵字來回傳資料。Generator方法特別適用在物件代表的是一個集合的情境,透過Generator方法可以將集合中的項目一一取出。參考以下範例程式碼 ,EmployeeList類別中宣告getIterator()方法,回傳Iterator,範例中使用new關鍵字建立EmployeeList物件,再叫用getIterator()方法,最後透過for of迴圈將Iterator所有的項目印出:

class EmployeeList {
*getIterator(){
  yield "Mary";
  yield "Candy";
  yield "Lili";
  yield "Vick";

}

}

let empList = new EmployeeList();
let ite = empList.getIterator();


for (let emp of ite) {
  console.log(emp);
}


此範例執行的結果將會依序印出以下字串:

Mary
Candy
Lili
Vick

 

定義類別預設的Iterator

若類別代表的是一堆項目所成的集合,你可以為類別定義預設的Iterator,這樣有助於從集合中將其中的項目取出。要定義類別預設的Iterator,可以使用Symbol.iterator定義一個Generator方法,用來取得預設的Iterator,參考以下範例程式碼,for of迴圈會自動利用我們定義的預設Iterator將其中的項目一一印出:

class EmployeeList {
  *[Symbol.iterator]() {
    yield "Mary";
    yield "Candy";
    yield "Lili";
    yield "Vick";
  }
}

let empList = new EmployeeList();
for (let emp of empList) {
  console.log(emp);
}

 

也可以使用Symbol.iterator取得預設的Iterator,參考以下範例程式碼:

 

class EmployeeList {
   *[Symbol.iterator]() {
     yield "Mary";
     yield "Candy";
     yield "Lili";
     yield "Vick";
   }
}

let empList = new EmployeeList();
let ite = empList[Symbol.iterator](); //取得預設的Iterator
for (let emp of ite) {
   console.log(emp);
}


 

靜態成員(Static Members)

在ECMAScript 5,直接在建構函式新增方法可以模擬物件導向語言中的靜態方法(Static Method),參考以下範例程式碼,為Employee定義一個getCompanyName()方法,取回公司名稱,後續則可以利用「Employee.getCompanyName()」語法直接叫用這個方法,叫用方法之前可以不必使用new關鍵字建立Employee物件:

 

function Employee(id, name) {
  this.id = id;
  this.name = name;
}
//靜態方法(Static Method)
Employee.getCompanyName = function () {
  return "UUU";
};

// 實體方法 Instance Method
Employee.prototype.printInfo = function () {
  console.log(this.id + " , " + this.name);
};

console.log(Employee.getCompanyName()); // UUU

var emp = new Employee(1, "Mary");
console.log(emp.id); // 1
console.log(emp.name);  // Mary   

 


ECMAScript 2015靜態成員(Static Members)

ECMAScript 2015新增static關鍵字,讓類別可以定義靜態成員,例如靜態方法(Static Method)、靜態存取子屬性(Static Accessor Property,又稱getter與setter),但目前並不支援靜態屬性的語法,在未來的版本中可能會支援。靜態方法(Static Method)要呼叫時直接使用「類別名稱.方法名稱」語法,不需要先建立物件實體。不過要注意,constructor方法不能夠使用static關鍵字。

通常靜態方法都是使用在公用程式,靜態方法會關聯到建構函式,而不是物件的prototype。參考以下範例程式碼,在Employee類別的宣告之中,定義一個getCompanyName()靜態方法,方法前方要加static關鍵字(static method中不能使用this存取Instance成員,你也不能透過物件的實體使用靜態成員):

class Employee {
   constructor(id, empName) {
     this.id = id;
     this.empName = empName;
   }
   printInfo() {
     console.log(this.id + " , " + this.empName);
   }
   static getCompanyName() {
     console.log(this.id); //undefined
     console.log(this.empName); //undefined

     return "UUU";
   }
}
console.log(Employee.getCompanyName()); //UUU

var emp = new Employee(1, "Mary");
console.log(emp.id); // 1
console.log(emp.empName);  // Mary

console.log(emp.printInfo === Employee.prototype.printInfo); // true
console.log(emp.constructor.getCompanyName == Employee.getCompanyName); // true

 

因為靜態方法是定義在Constructor function中,因此「emp.constructor.getCompanyName == Employee.getCompanyName」比較的結果會是「true」;而printInfo()實體方法是定義在prototype物件中,因此「emp.printInfo === Employee.prototype.printInfo」比較的結果會是「true」。

 

靜態屬性

但目前ECMAScript 2015並不支援使用static關鍵字,定義靜態屬性的語法,在未來的版本中可能會支援,目前可以直接用「Class名稱.屬性名稱」來定義靜態屬性,參考以下範例程式碼:

class Employee {
  constructor(id, empName) {
    this.id = id;
    this.empName = empName;
  }
  printInfo() {
    console.log(this.id + " , " + this.empName);
  }
}
Employee.CompanyName = "UUU";
console.log(Employee.CompanyName); // UUU

 

靜態存取子屬性

靜態存取子屬性又稱getter與setter,語法與ECMAScript 5 Object Litaral Notation語法類似,將上個範例改用static getter語法改寫如下,讀取靜態存取子屬性時,同樣使用「類別名稱.屬性名稱」語法:

class Employee {
   constructor(id, empName) {
     this.id = id;
     this.empName = empName;
   }
   printInfo() {
     console.log(this.id + " , " + this.empName);
   }

   static get CompanyName() {
     return "UUU";
   }
}

console.log(Employee.CompanyName); // UUU

Tags:

.NET Magazine國際中文電子雜誌 | JavaScript | 許薰尹Vivid Hsu

評論 (45) -

cours de theatre
cours de theatre United States
2017/9/30 下午 04:56:48 #

Great, thanks for sharing this article.Really looking forward to read more. Fantastic.

回覆

buy hacklink google
buy hacklink google United States
2017/10/9 下午 01:57:25 #

Im obliged for the article.Really looking forward to read more. Great.

回覆

kamagra sklep
kamagra sklep United States
2017/10/9 下午 03:34:45 #

Muchos Gracias for your post.

回覆

Osimi seaview
Osimi seaview United States
2017/10/9 下午 05:51:19 #

Awesome article post. Cool.

回覆

solarmovie
solarmovie United States
2017/10/10 下午 07:39:33 #

Thanks a lot for the article post. Keep writing.

回覆

buy hacklinks
buy hacklinks United States
2017/10/12 下午 08:30:02 #

Thanks for the blog.Thanks Again.

回覆

reference
reference United States
2017/10/14 下午 03:45:39 #

Im obliged for the blog article.Really looking forward to read more. Want more.

回覆

dragon city hack apk
dragon city hack apk United States
2017/10/15 下午 03:14:09 #

Really appreciate you sharing this blog article.Really thank you! Fantastic.

回覆

omega xl review
omega xl review United States
2017/10/15 下午 07:55:33 #

Im obliged for the article post.Much thanks again. Keep writing.

回覆

useful link
useful link United States
2017/10/17 下午 02:32:37 #

Im thankful for the blog article.Much thanks again. Cool.

回覆

sletrokor
sletrokor United States
2017/10/17 下午 08:03:49 #

A round of applause for your post.Thanks Again. Great.

回覆

VigRx Plus
VigRx Plus United States
2017/10/19 上午 07:08:17 #

I really enjoy the post.Really looking forward to read more. Much obliged.

回覆

More Info
More Info United States
2017/10/19 下午 06:00:52 #

Really informative blog.Really looking forward to read more.

回覆

pure slim 1000
pure slim 1000 United States
2017/10/20 上午 03:31:43 #

Looking forward to reading more. Great blog post.Really thank you! Really Cool.

回覆

Osimi Sea View
Osimi Sea View United States
2017/10/21 上午 03:15:05 #

I truly appreciate this post.Really thank you! Awesome.

回覆

website designing company in Delhi India
website designing company in Delhi India United States
2017/10/24 上午 11:51:15 #

Thank you ever so for you blog.Much thanks again. Want more.

回覆

EZ Battery Reconditioning
EZ Battery Reconditioning United States
2017/10/30 上午 09:31:21 #

I appreciate you sharing this blog post.Really thank you! Cool.

回覆

life leadership
life leadership United States
2017/11/1 上午 09:44:24 #

Awesome post.Really thank you! Awesome.

回覆

phenocal
phenocal United States
2017/11/1 下午 05:11:41 #

Major thanks for the blog article.Much thanks again. Cool.

回覆

phentaslim
phentaslim United States
2017/11/3 上午 11:58:07 #

I appreciate you sharing this article.Much thanks again. Really Great.

回覆

home remedies for back pain
home remedies for back pain United States
2017/11/15 上午 10:34:48 #

Major thanks for the post.Really looking forward to read more. Really Cool.

回覆

http://mosier.dokterchiropractic.com/
http://mosier.dokterchiropractic.com/ United States
2017/11/17 下午 07:43:02 #

I loved your article.Really thank you! Fantastic.

回覆

bikinis
bikinis United States
2017/11/24 上午 12:42:57 #

Very informative article post.Much thanks again. Want more.

回覆

fake cash for cars
fake cash for cars United States
2017/11/29 下午 07:22:07 #

Im thankful for the post. Will read on...

回覆

porno
porno United States
2017/12/1 下午 07:27:50 #

I think this is a real great blog article. Will read on...

回覆

building business credit
building business credit United States
2017/12/3 上午 07:38:09 #

I loved your blog.Thanks Again. Keep writing.

回覆

free android porn
free android porn United States
2017/12/5 下午 12:29:33 #

Thanks-a-mundo for the article.Thanks Again.

回覆

I really like and appreciate your blog article.Much thanks again. Cool.

回覆

Francis Balette
Francis Balette United States
2017/12/14 下午 12:08:22 #

Im obliged for the blog post.Much thanks again. Fantastic.

回覆

article
article United States
2017/12/14 下午 07:04:50 #

Major thankies for the article post.Much thanks again. Will read on...

回覆

Christmas Music
Christmas Music United States
2017/12/15 上午 01:39:53 #

Appreciate you sharing, great post.Much thanks again. Cool.

回覆

canon driver software
canon driver software United States
2017/12/16 下午 08:42:53 #

Fantastic article.Really looking forward to read more. Really Great.

回覆

green coffee bean for weight loss
green coffee bean for weight loss United States
2017/12/17 上午 03:06:56 #

Really enjoyed this blog article. Much obliged.

回覆

Wow, great post.Really looking forward to read more. Really Great.

回覆

Departments
Departments United States
2017/12/17 下午 08:17:25 #

Really informative blog article.Really thank you! Really Great.

回覆

canon drivers
canon drivers United States
2017/12/23 上午 05:11:37 #

Major thanks for the post.Thanks Again. Really Cool.

回覆

http://hs21.cn/comment/html/?163181.html
http://hs21.cn/comment/html/?163181.html United States
2017/12/24 下午 04:09:18 #

Thanks-a-mundo for the article. Really Cool.

回覆

hp driver
hp driver United States
2017/12/25 下午 05:13:54 #

Very neat post. Fantastic.

回覆

Say, you got a nice article.Much thanks again. Much obliged.

回覆

canon printer series
canon printer series United States
2017/12/27 下午 06:57:22 #

Really appreciate you sharing this article post.Really thank you! Really Cool.

回覆

click for source
click for source United States
2018/1/2 下午 05:40:35 #

I really like and appreciate your post. Much obliged.

回覆

Thank you ever so for you article post. Will read on...

回覆

hp drivers
hp drivers United States
2018/1/3 上午 08:59:41 #

I think this is a real great blog.Really thank you! Awesome.

回覆

colocation chicago
colocation chicago United States
2018/1/10 上午 10:54:09 #

I really enjoy the blog.Thanks Again. Want more.

回覆

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List