深入理解ES6 009【學習筆記】

JavaScript 中的類別

function PersonType(name){  this.name = name;}PersonType.prototype.sayName = function(){  console.log(this.name)}var person = new PersonType("Nicholas")person.sayName(); // outputs "Nicholas"console.log(person instanceof PersonType) // trueconsole.log(person instanceof Object) // true

類別宣告

class PersonClass {  // 等價於PersonType建構函式  constructor(name){    this.name = name  }  // 等價於PersonType.prototype.sayName  sayName(){    console.log(this.name)  }}let person = new PersonClass("Nicholas");person.sayName(); // outputs "Nicholas"console.log(person instanceof PersonClass) //trueconsole.log(person instanceof Object) // trueconsole.log(typeof PersonClass) // functionconsole.log(typeof PersonClass.prototype.sayName) // function

class是語法糖,PersonClass 宣告實際上建立了一個具有建構函式方法行為的函式。透過Symbol.iterator定義產生器方法即可定義預設迭代器

class Collection {
  constructor(){
    this.items = [];
  }

  *[Symbol.iterator](){
    yield *this.items.values()
  }
}
var collection = new Collection();
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);

for(let x of collection){
  console.log(x);
}
// 輸出:
// 1
// 2
// 3

靜態成員

ES6 類別語法簡化了建立靜態成員的過程,在方法或存取器屬性名前使用正式的靜態註釋即可。

class PersonClass {
  // 等價於PersonType建構函式
  constructor(name){
    this.name = name
  }
  // 等價於PersonType.prototype.sayName
  sayName(){
    console.log(this.name)
  }
  // 等價於等價於PersonType.create
  static create(name){
    return new PersonClass(name)
  }
}
let person = PersonClass.create("Nicholas")

繼承與衍生類別

實作繼承與自訂型別是一項不小的工作,嚴格意義上的繼承需要多個步驟來實作

function Rectangle(length,width){
  this.length = length;
  this.width = width;
}
Rectangle.prototype.getArea = function(){
  return this.length*this.width
}

function Square(length){
  Rectangle.call(this,length,length)
}

Square.prototype = Object.create(Rectangle.prototype,{
  constructor:{
    value:Square,
    enumerable:true,
    writable:true,
    configurable:true
  }
})
var square = new Square(3)
console.log(square.getArea()); // 9
console.log(square instanceof Square) // true
console.log(square instanceof Rectangle) // true

ES6 使用extends關鍵字可以指定類別繼承的函式。原型會自動調整,透過呼叫 super() 方法即可存取基底類別的建構函式

class Rectangle {
  constructor(length,width){
    this.length = length;
    this.width = width
  }
  getArea(){
    return this.length*this.width;
  }
}
class Square extends Ractangle {
  constructor(length){
    // Rectangle.call(this,length,length)
    super(length,length)
  }
}
var square = new Square(3)
console.log(square.getArea()); // 9
console.log(square instanceof Square) // true
console.log(square instanceof Rectangle) // true

繼承自其的類別我們稱之為衍生類別,如果在衍生類別中指定了建構函式,則必須呼叫 super(),如果沒有這樣做,程式就會報錯。

  • super
  • 只能在衍生類別的建構函式中使用 super(),非衍生類別不可使用
  • 在建構函式中存取 this 之前一定要呼叫 super,它負責初始化 this,
  • 如果不想呼叫 super(),則唯一的方法是讓類別的建構函式回傳一個物件。

類別方法遮蔽

衍生類別中的存取總是會覆寫基底類別中的同名方法,如果想呼叫基底類別中的方法,我們可以使用super.getArea()

class Square extends Rectangle {
  constructor(length){
    super(length,length)
  }
  //覆蓋並遮蔽Rectangle.prototype.getArea()方法
  getArea(){
    return this.length*this.length
  }
}

衍生自表達式的類別

ES6 最強大的一面或許是從表達式匯出類別的功能了。只要表達式可以被解析為一個函式並且具有[[Construct]]屬性與原型,那麼就可以用 extends 進行衍生。

內建物件的繼承

class MyArray extends Array {
  // 空
}
var colors = new MyArray();
colors[0]="red"
console.log(colors.length) // 1
colors.length = 0
console.log(colors[0]); // undefined

MyArray 直接繼承自 Array,其行為與 Array 也很相似,操作數值型屬性會更新 length 屬性,操作 length 屬性也會更新數值型屬性。於是,可以正確地繼承 Array 物件來建立自己的衍生陣列型別

Symbol.species 屬性

內建物件繼承的一個實用之處是,原本在內建物件中回傳實例自身的方法將自動回傳衍生類別的實作。例如,如果你有一個繼承自 Array 的衍生類別 MyArray,那麼像 slice() 這樣的方法也會回傳一個 MyArray 的實例

class MyArray extends Array{
  // 空
}
let items = new MyArray(1,2,3,4),
subitems = items.slice(1,3);
console.log(items instanceof MyArray); //true
console.log(subitems instanceof Myarray); // true

Symbol.species是諸多內部 Symbol 中的一個,它被用於定義回傳函式的靜態存取器屬性。被回傳的函式是一個建構函式,每當要在實例方法中(不是建構函式中)建立類別的實例時,必須使用這個建構函式。以下這些都是實作該屬性定義的Symbol.species

  • Array
  • ArrayBuffer
  • Map
  • Promise
  • RegExp
  • Set
  • Typed arrays
// 幾個內建型別像這樣使用
class MyClass {
  static get [Symbol.species](){
    return this
  }
  constructor(value){
    this.value = value
  }
  clone(){
    return new this.constructor[Symbol.species](this.value)
  }
}

在建構函式中使用 new.target

class Shape {
  constructor(){
    if(new.target === Shape){
      throw new Error("這個類別是不能直接被實例化的")
    }
  }
}
class Rectangle extends Shape {
  constructor(length,width){
    super();
    this.length = length;
    this.width = width;
  }
}
let x = new Shap(); // 拋出錯誤
let y = new Rectangle(3,4) // 正常執行
console.log(y instanceof Shape) //true

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://www.walker-learn.xyz/archives/4335

(0)
Walker的頭像Walker
上一篇 2025年3月8日 12:52
下一篇 2025年3月8日 12:51

相關推薦

  • Go工程師體系課 009【學習筆記】

    其他一些功能 個人中心 收藏 管理收貨地址(增刪改查) 留言 複製inventory_srv--> userop_srv 查詢替換所有的inventory Elasticsearch 深度解析文件 1. 什麼是Elasticsearch Elasticsearch是一個基於Apache Lucene建構的分散式、RESTful搜尋和分析引擎,能夠快速地…

    個人 2025年11月25日
    33800
  • 【開篇】

    我是Walker,生於八十年代初,程式碼與生活的旅者。全端開發工程師,遊走於前端與後端的邊界,執著於技術與藝術的交會點。程式碼,是我編織夢想的語言;專案,是我刻畫未來的畫布。在鍵盤的敲擊聲中,我探索技術的無限可能,讓靈感在程式碼裡永恆綻放。深度咖啡愛好者,迷戀每一杯手沖的詩意與儀式感。在咖啡的醇香與苦澀中,尋找專注與靈感,亦如在開發的世界中追求極致與平衡。騎…

    2025年2月6日 個人
    2.4K00
  • 深入理解ES6 011【學習筆記】

    Promise與異步程式設計 因為執行引擎是單執行緒的,所以需要追蹤即將執行的程式碼,那些程式碼會被放在一個任務佇列中,每當一段程式碼準備執行時,都會被新增到任務佇列中,每當引擎中的一段程式碼結束執行時,事件迴圈會執行佇列中的下一個任務。 Promise 相當於異步操作結果的佔位符,它不會去訂閱一個事件,也不會傳遞一個回呼函式給目標函式,而是讓函式回傳一個 Promise,就像這樣…

    個人 2025年3月8日
    1.2K00
  • Go工程師體系課 001【學習筆記】

    轉型 想在短時間系統轉到Go工程的理由 提高CRUD,無自研框架經驗 提升技術深度,做專、做精需求的同學 進階工程化,擁有良好開發規範和管理能力的 工程化的重要性 高階開發的期望 良好的程式碼規範 深入底層原理 熟悉架構 熟悉k8s的基礎架構 擴展知識廣度,知識的深度,規範的開發體系 四個大的階段 Go語言基礎 微服務開發的(電商專案實戰) 自研微服務 自研然後重…

    個人 2025年11月25日
    37400
  • 深入理解ES6 008【學習筆記】

    迭代器(Iterator)和產生器(Generator)這項新特性對於高效的資料處理而言是不可或缺的,你也會發現在語言的其他特性中也都有迭代器的蹤影:新的 for-of 迴圈、展開運算子 (...)、甚至連非同步程式設計都可以使用迭代器。 迭代器是一種特殊的物件,它具有一些專門為迭代過程設計的專有介面,所有的迭代器物件都有一個 next() 方法,每次呼叫都傳回一個結果對…

    個人 2025年3月8日
    1.2K00
簡體中文 繁體中文 English