首頁  >  文章  >  web前端  >  es6 class是語法糖嗎

es6 class是語法糖嗎

青灯夜游
青灯夜游原創
2022-10-17 18:03:101822瀏覽

class是語法糖。原因:class是基於原型繼承的實現,對語言的功能並沒有什麼影響,只是方便了語法的書寫及閱讀;class的本質是function,能夠讓物件原型的寫法更清晰,更像物件導向程式設計的語法。

es6 class是語法糖嗎

本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。

ES6 class類別-語法糖

class (類別)作為物件的模板被引入,可以透過 class 關鍵字定義類別。它的本質是函數(function),可以看作一個語法糖,讓物件原型的寫法更加清晰、更像物件導向程式設計的語法。

它的class和別的語言不一樣,它依舊是基於原型繼承的實現,對語言的功能並沒有影響,只是方便了你的書寫及閱讀

為什麼說ES6的class是語法糖

我們帶著問題去閱讀下文:

  • 為什麼說ES6的class是語法糖?
  • class是原型的語法糖嗎?
  • 那又是如何使用原型來實現class這一語法糖的呢?

1. 基於Prototype的OOP

#先來看一個prototype的範例:

function Person (name, sex) {
	this.name = name
	this.sex = sex
}
 
function Man (name) {
	this.name = name
}
 
Man.prototype = new Person('', 'male')
 
let Jy = new Man('Jy')
 
console.log(Jy.name, Jy.sex) // Jy, male

這是我們使用原型的一個很簡單的例子,Person有名字和性別,Man是性別為男的Person,Jy是一個Man。我們先記住這一個例子,以下將使用class重寫這個例子。

Tips: new, this等是Brendan Eich使之更像Java的OOP而加上的,有興趣的讀者可以自行查閱相關資訊。

2. ES6 Class的OOP

#
class Person {
	constructor (name, sex) {
		this.name = name
		this.sex = sex
	}
}
 
class Man extends Person {
	constructor (name) {
		super('', 'male')
		this.name = name
	}
}
 
let Jy = new Man('Jy')
 
console.log(Jy.name, Jy.sex) // Jy, 'male'

我們透過重寫這個例子,採用了class、constructor、extends、super這些單字,接下來就具體來說說ES6規範中對它們做了什麼。

3. 使用Prototype實作的Class OOP(ES6規格)

在ES6之前,JS物件其實就是屬性的集合,而屬性則是一組鍵值對(key, value),key可以是String or Symbol, value則包含資料屬性特徵值與存取器特徵值。

你說普通的屬性還好,不還有物件下面的方法嗎?怎麼就變成屬性的集合了呢?

其實在ES5規範中出現的method的定義是“function that is the value of a property”,是對象的函數屬性而已,不能稱之為方法,直到ES6出現,規範中才有Method Definitions。

我們能想到的在ES3有關OOP的東西: prototype、new、 this、 constructor、 instanceof, 甚至不是規範的 __proto__ 屬性。

所幸的是在ES5中我們增加了很多方法來補全它,使之完備:

  • Object.defineProperty
  • Object.freeze
  • Object.create
  • Object.getPrototypeOf
  • Object.setPrototypeOf
  • isPrototypeOf
  • ......

#再來看一段程式碼:

let obj = {
	name: 'Jy',
	speak () { // Note: it's not speak: function () {}
		console.log(this.name, super.name)
	}
}
 
obj.speak() // Jy, undefined
 
Object.setPrototypeOf(obj,  { name: 'super' })
 
obj.speak() // Jy, super
 
let speak = obj.speak
speak() // undefined, super

obj.speak在ES6中定義已經是Method了,它具有屬性[[homeObject]],homeObject指向方法被呼叫的物件(程式碼中指的是obj),它是綁定在物件中的Internal Slots,也就是你不能去修改,就等於寫死了。

那homeObject有什麼用呢?它跟super密切相關,當解析到super這個關鍵字的時候就會找homeObject的prototype。

簡單來說,總結為下面兩個公式:

  • let homeObj = Method[[HomeObject]] = obj
  • super = Object.getPrototypeOf(homeObj )

Note: homeObject是靜態綁定在internal slots中的,而super是動態尋找的。

講完super,我們來講講extends和constructor

class A extends B { }
 
class A extends B {
	constructor (...args) {
		super(args)
	}
}
 
class C extends null { }

extends主要做了以下兩件事:

  • Object.setPrototypeOf(A , B)
  • Object.setPrototypeOf(A.prototype, B.prototype)

如果父類別是null, 則執行Object.setPrototypeOf(C.prototype, null)

上述程式碼的第一和第二部分差別在於有沒有顯示宣告constructor, 那麼這兩段程式碼是否等價呢?答案是等價的。

規範中就是這麼定義的:

程式碼的第三部分是繼承了null, 它不會報語法錯誤,但是我們無法new一個C出來,原因是new的時候會呼叫null的constructor,而null沒有constructor。

看到這裡,ES6的class oop, 規格宣告都是使用原型來操作,所以我們是不是可以說class是原型的語法糖了?

4. babel編譯後的class

我們在實際專案中多採用babel來編譯ES6、7的程式碼,所以這節我們就來分析以下babel編譯後的程式碼,其中會省略一些報錯、類型偵測的一些相關程式碼來更好地呈現使用原型來實現OOP的主題。

編譯前:

class A extends B {}
 
console.log(new A)

編譯後:

"use strict";
 
function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
    return o.__proto__ || Object.getPrototypeOf(o);
  };
  return _getPrototypeOf(o);
}
 
function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      writable: true,
      configurable: true
    }
  });
  if (superClass) _setPrototypeOf(subClass, superClass);
}
 
function _setPrototypeOf(o, p) {
  _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
    o.__proto__ = p;
    return o;
  };
  return _setPrototypeOf(o, p);
}
 
var A =
  /*#__PURE__*/
  function (_B) {
    _inherits(A, _B);
 
    function A() {
 
      return _getPrototypeOf(A).apply(this, arguments);
    }
 
    return A;
  }(B);
 
console.log(new A());

我们重点看_inherits 方法,跟我们上述说的extends做的两件事是一样的:

  • Object.setPrototypeOf(subClass, superClass)
  • Object.setPrototypeOf(subClass.prototype, superClass.prototype)

只不过它采用的是Object.create方法,这两个方法的区别可以去MDN上查看。

再看function A内部,其实就是执行了B的构造器函数来达到super(arguments)的效果, 这个与规范:如果没有显示声明constructor会自动加上constructor是一致的。

5. 总结

至此,我们终于理解了为什么class是原型的语法糖以及如何使用原型来实现class这一语法糖。

但切记我们使用原型的目的并不是来模拟class oop的,prototype based的oop应该用prototype去理解而不是class。

ES6的class oop 是不完备的 ,例如abstract class 、interface、private等都还没有,不过有些功能已经在提案中了,大家可以拥抱它,或者TypeScript是个不错的选择,如果你的项目中使用到了TS, 欢迎你到评论区分享你的感受。

【相关推荐:javascript视频教程编程视频

以上是es6 class是語法糖嗎的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn