背景
在ES6之前,官方都不支援類的語法,很多第三方類庫都通過模擬實現了類似類的用法,最終ES6引入了類
類的介紹
類的宣告
ES6之前的寫法:稱為自定義型別
function Person(name){
this.name = name
}
Person.prototype.sayName = function(){
return this.name
}
複製程式碼
ES6的寫法:
class Person {
//構造器
constructor(name){
this.name = name //自有屬性
}
sayName(){
return this.name
}
}
let person = new Person('Tom')
console.log(person.sayName()) //Tom
console.log(person instanceof Person) //true
console.log(person instanceof Object) //true
console.log(typeof Person) //function
console.log(typeof Person.sayName) //undefined
console.log(typeof Person.prototype.sayName) //function
複製程式碼
console.log(typeof Person) //function
通過列印結果可以看到,ES6對類的宣告類似ES5自定義型別寫法的語法糖,本質上class定義的Person仍然是一個函式console.log(typeof Person.sayName) //undefined console.log(typeof Person.prototype.sayName) //function
通過列印結果可以看到,ES6類中宣告的方法實際也是掛載到原型上的,這個ES5自定義型別中定義的方法是一樣的
類表示式:
let Person = class {
//構造器
constructor(name){
this.name = name //自有屬性
}
sayName(){
return this.name
}
}
//具名類表示式
let Person = class Person2{
//構造器
constructor(name){
this.name = name //自有屬性
}
sayName(){
return this.name
}
}
**這裡的Person2只能在類的方法中被訪問到**
複製程式碼
使用ES6宣告類的優勢
- 類宣告不會被提升,類宣告類似於let,在程式執行到達類宣告之前,是處於暫時性死區中的,訪問會報錯。ES5中的函式定義法,函式是可以被提升到當前作用域頂部的
- 類宣告中的程式碼強制在嚴格模式下執行'use strict',且不能退出
- 類的所有方法不可列舉,ES5的自定義型別只能通過Object.defineProperty()來定義屬性描述符規定其不可列舉
- 呼叫類生成例項時,必須使用new關鍵詞,會自動呼叫類中的構造器constructor函式。不適用new 呼叫類會報錯;這是自定義型別的函式是沒辦法規避的
- 類的方法不能使用new 關鍵詞呼叫,會丟擲錯誤
- 試圖在類的方法中重寫類名(相當於const類名),會丟擲錯誤;在類外部可以重寫類名(相當於let類名)
類是一等公民
在程式設計中,能被當做值來使用的就稱為一等公民,也就是說,就像函式那樣,能夠作為函式的引數、函式的返回值、能用來給變數賦值等操作
function createClass(classDef){
return new classDef()
}
let obj = createClass(class {
sayHi(){
console.log('Hi')
}
})
obj.sayHi() //Hi
複製程式碼
類的訪問器屬性
class Person {
constructor(name){
this.value = name
}
get name(){
console.log('getter')
return this.value
}
set name(name){
console.log('setter')
this.value = name
}
}
let person = new Person('Tom')
console.log(person.name)
person.name = 'Lisa'
console.log(person.name)
/*
getter
Tom
setter
getter
Lisa
*/
複製程式碼
為類新增生成器方法(定義Symbol.iterator)
class Collection {
constructor(){
this.items= []
}
*[Symbol.iterator](){
yield *this.items.values() //this.items.values()是陣列內建的迭代器,這裡相當於把this.items的生成器合併在類生成器中了
}
}
let collection = new Collection()
collection.items.push(1)
collection.items.push(2)
collection.items.push(3)
for(let value of collection){
console.log(value)
}
/*
1
2
3
*/
複製程式碼
靜態成員
我們之前已經實驗過,在類中定義的方法,是把此方法屬性加到類的原型上的,類的例項也都繼承了這些方法,可以訪問; 如果不希望例項可以訪問,只能被類自身訪問,則需要使用靜態成員
ES5傳統的實現寫法
function Person(name){
this.name = name
}
//新增在原型上,所有例項都可以訪問
Person.prototype.getName = function(){
return this.name
}
靜態方法,例項訪問不到
Person.sayHi = function(){
console.log('Hi')
}
複製程式碼
ES6寫法
class Person {
constructor(name){
this.name = name
}
//新增到原型上,所有例項都可以訪問
getName(){
return this.name
}
static sayHi(){
console.log('Hi')
}
}
let person = new Person('Tom')
console.log(person.getName()) //Tom
// person.sayHi() // 丟擲異常 person.sayHi is not a function
Person.sayHi()//Hi
複製程式碼
使用extends關鍵詞完成類的繼承
class Rectangle {
constructor(length, width){
this.length = length;
this.width = width;
}
getArea(){
return this.length * this.width
}
}
class Square extends Rectangle {
constructor(length){
//使用super()呼叫父類的建構函式
super(length, length)
}
}
let square = new Square(3)
console.log(square.getArea()) //9
console.log(square instanceof Square) //true
console.log(square instanceof Rectangle) //true
複製程式碼
- 繼承父類的類叫派生類,派生類如果顯式定義了構造器,就必須在訪問this之前,要顯式地使用super()呼叫父類的構造器,完成父類初始化this,並通過繼承傳遞給派生類,否則在派生類中訪問this時會報錯
- 在派生類中才可以使用super關鍵詞,指代父類,super()表示先初始化父類的構造器,然後派生類繼承父類的this,所以在派生類中this也就可以訪問了
class Rectangle { constructor(){ console.log('call parent') this.length = 10 this.width = 5 } getArea(){ return this.length * this.width } } class Square extends Rectangle { constructor(){ console.log('call child1') super() console.log('call child2') } getValue(){ console.log('length',this.length) console.log('width',this.width) } } let square = new Square() square.getValue() /* call child1 call parent call child2 length 10 width 5 */ 複製程式碼
- 如果派生類沒有顯式定義構造器,則系統在初始化的時候會預設呼叫super(),並使用建立例項時傳遞的所有引數
class Square extends Rectangle { } // 等價於 class Square extends Rectangle { constructor(..args){ super(...args) } } 複製程式碼
注意:如果派生類與父類初始化的引數完全相同,可以選擇省略constructor的形式
class Rectangle {
constructor(length, width){
this.length = length;
this.width = width;
}
getArea(){
return this.length * this.width
}
}
class Square extends Rectangle {
}
let square = new Square(3,3)
console.log(square.getArea()) //9
複製程式碼
如果派生類與父類的初始化引數不一致,就得自己寫構造器constructor,手動呼叫super(),否則會得到意想不到的結果
class Rectangle {
constructor(length, width){
this.length = length;
this.width = width;
}
getArea(){
return this.length * this.width
}
}
class Square extends Rectangle {
}
let square = new Square(3) //這裡只傳了1個引數,派生類使用預設的構造器傳遞引數,與父類構造器引數不一致,導致有些引數為undefined,呼叫計算方法是肯定為NaN
console.log(square.getArea()) //NaN
複製程式碼
靜態成員也會被繼承
class Person {
constructor(name){
this.name = name
}
static sayHi(){
console.log('Hi')
}
}
class Student extends Person {
}
Person.sayHi()//Hi
Student.sayHi()//Hi
複製程式碼