ES5中的類
在ES5宣告一個函式(可以new),再將方法新增到這個方法的原型上,來建立自定義型別。
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log(this.name);
};
let person = new Person("xunuo0x");
person.sayName(); // 輸出 "xunuo0x"
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
複製程式碼
ES6中類的宣告
本質:ES5實現方式的語法糖
我們拿下面用class
宣告的Person
為例:也就是說Person
類為一個具有建構函式行為的函式,其中內部方法sayName
實際上就是Person.prototype.sayName()
。所以說本質上是ES5實現方式的語法糖。
console.log(typeof Person) // 'function'
複製程式碼
區別在於,類的屬性不可重新賦值和不可列舉的,Person.prototype
就是一個只讀屬性。
宣告一個類
宣告:
- 構造器:構造器內建立自有屬性
- 方法:宣告類例項具有的方法
class Person {
// 等價於 Person 構造器
constructor(name) {
this.name = name;
}
// 更加簡單的宣告類的內部函式
// 等價於 Person.prototype.sayName
sayName() {
console.log(this.name);
}
}
let person = new Person("xunuo0x");
person.sayName(); // 輸出 "xunuo0x"
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
console.log(typeof Person); // "function"
console.log(typeof Person.prototype.sayName); // "function"
複製程式碼
class和自定義型別的區別
- class的宣告不會提升,與let類似
- class的宣告自動執行於嚴格模式之下
- class宣告的方法不可列舉(顯著區別)
- class的內部方法沒有[[construct]]屬性,無法new
- 呼叫class的建構函式必須new
- class內部方法不能同名
用ES5重寫如下:
在實現的時候,主要使用Object.defineProperty()
實現class內部函式
// 直接等價於 Person
let Person2 = (function() {
"use strict";
// 有個同名的只讀內部函式
// **類的內部不能修改類名**
const Person2 = function(name) {
// 確認函式被呼叫時使用了 new
if (typeof new.target === "undefined") {
throw new Error("Constructor must be called with new.");
}
this.name = name;
}
Object.defineProperty(Person2.prototype, "sayName", {
value: function() {
// 確認函式被呼叫時沒有使用 new
if (typeof new.target !== "undefined") {
throw new Error("Method cannot be called with new.");
}
console.log(this.name);
},
// **類的內部方法定義為不可列舉**
enumerable: false,
writable: true,
configurable: true
});
return Person2;
}());
複製程式碼
類表示式
- 匿名類表示式
let Person = class{...}
- 具名類表示式
let Person = PersonClass class{...}
- 區別僅在於class的內部實現時,
const PersonClass
作為內部實現的類名
class作為一級公民
js中能當作值來使用的稱為一級公民
用法:
- 類名作為引數傳入函式
- 立即執行,實現單例模式
// 類名作為引數傳入函式
function crateObj (ClassName){
return new ClassName()
}
// 立即執行,實現單例模式
let person = new class {
constructor (name) {
this.name = name
}
say () {
console.log(this.name)
}
}('xunuo0x')
person.say() // "xunuo0x"
複製程式碼
class中訪問器屬性
- get 關鍵字
- set 關鍵字
- 內部實現時將getter/setter變數名,通過
Object.defineProperty()
定義
class中靜態成員
- static關鍵字
- 相當於ES5中
Person.create() = function() {}
- 訪問時直接通過類訪問,不能通過例項訪問
使用extends繼承
只要一個表示式能返回具有
[[constructor]]
就可以使用extends
繼承;也就是說可以繼承一個函式
ES5中的繼承
function Parent (name) {
this.name = name
}
Parent.prototype.sayName = function () {
console.log(this.name)
}
function Child (name) {
Parent.call(this, name)
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
複製程式碼
在ES6中的繼承
class Parent (name) {
constructor (name) {
this.name = name
}
sayName() {
console.log(this.name)
}
}
class Child extends Parent (name) {
constructor(name) {
super(name)
}
// 重寫父類中的方法
sayName () {
console.log(`Child ${this.name}`)
}
}
複製程式碼
繼承內建物件
- ES5中繼承內建物件(如繼承Array可能會產生問題)
- ES5中繼承,this先被派生類建立
- ES6中繼承,this先被基類建立,就具有了基類的方法和屬性
Symbol.species
屬性
- extends繼承時,派生類上返回的是派生類的例項
- 如果想返回基類,可以設定
Symbol.species
class MyClass extends Array {
static get [Symbol.species]() {
return this; // 預設返回MyClass型別
return Array; // 修改返回基類
}
constructor(value) {
this.value = value;
}
}
複製程式碼
new.target
- 見名知意,就是new操作執行的物件
- ES6中例項化class時,必須要new,所以在
constructor()
中new.target
不可能是undefined
mixin繼承
function mixin (...mixin) {
var base = function() {}
Object.assign(base, ...mixin)
return mixin
}
class Person extends mixin(Animal, Monkey) {
constructor(){
super(Animal, Monkey)
// ......
}
}
複製程式碼
小結
- ES6中
class
簡化了ES5中的繼承,但是未改變現有的繼承模型。可以理解為是ES5基於原型鏈的語法糖 - 通過
class
宣告一個類,constructor()
作為建構函式,屬性在constructor()
中初始化 class
內可以定義getter/setter
訪問器屬性- 可以在
class
內定義非靜態方法,靜態方法繫結在構造器上 - 類的所有方法都是不可列舉的,也符合內部方法
- 例項化一個
class
必須要new
關鍵字 extends
實現繼承,子類中呼叫super()
訪問父類建構函式- 因為class的實現是基於ES5類模型那一套,本質上和ES5中是一樣的,如果過多使用
extends
可能還會降低效能