前端JavaScript中的class類
1、類
類是用於創建對象的模板。JavaScript
中生成對象實例的方法是通過構造函數,這跟主流面向對象語言(java
,C#
)寫法上差異較大,如下:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')'; }; var p = new Point(1, 1);
ES6
提供瞭更接近Java
語言的寫法,引入瞭 Class(類)
這個概念,作為對象的模板。通過class關鍵字,可以定義類。
如下:constructor()
是構造方法,而this代表實例對象:
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } }
類的數據類型就是函數,它本身就是指向函數的構造函數:
// ES5 函數聲明 function Point() { //... } // ES6 類聲明 class Point { //.... constructor() { } } typeof Point // "function" Point === Point.prototype.constructor // true
在類裡面定義的方法是掛到Point.prototype
,所以類隻是提供瞭語法糖,本質還是原型鏈調用。
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } Point.prototype = { //.... toString() } var p = new Point(1, 1); p.toString() // (1,1)
類的另一種定義方式類表達式
// 未命名/匿名類 let Point = class { constructor(x, y) { this.x = x; this.y = y; } }; Point.name // Point
函數聲明和類聲明有個重要區別,函數聲明會提升,類聲明不會提升。
> let p = new Point(); // 被提升不會報錯 > function Point() {} > > let p = new Point(); // 報錯,ReferenceError > class Point {} >
1.1 constructor()
constructor()
方法是類的默認方法,new
生成實例對象時會自動調用該方法。
一個類必須有constructor()
方法,如果沒有顯式定義,引擎會默認添加一個空的constructor()
。
constructor()
方法默認返回實例對象(即this
)。
class Point { } // 自動添加 class Point { constructor() {} }
1.2 getter和setter
與 ES5
一樣,在類的內部可以使用get
和set
關鍵字,對某個屬性設置存值函數和取值函數,攔截該屬性的存取行為。
class User { constructor(name) { this.name = name; } get name() { return this.name; } set name(value) { this.name = value; } }
1.3 this
類的方法內部的this
,它默認指向類的實例,在調用存在this的方法時,需要使用 obj.method()
方式,否則會報錯。
class User { constructor(name) { this.name = name; } printName(){ console.log('Name is ' + this.name) } } const user = new User('jack') user.printName() // Name is jack const { printName } = user; printName() // 報錯 Cannot read properties of undefined (reading 'name')
如果要單獨調用又不報錯,一種方法可以在構造方法裡調用bind(this)
。
class User { constructor(name) { this.name = name; this.printName = this.printName.bind(this); } printName(){ console.log('Name is ' + this.name) } } const user = new User('jack') const { printName } = user; printName() // Name is jack
bind(this)
會創建一個新函數,並將傳入的this作為該函數在調用時上下文指向。
另外可以使用箭頭函數,因為箭頭函數內部的this總是指向定義時所在的對象。
class User { constructor(name) { this.name = name; } printName = () => { console.log('Name is ' + this.name) } } const user = new User('jack') const { printName } = user; printName() // Name is jack
1.4 靜態屬性
靜態屬性指的是類本身的屬性,而不是定義在實例對象this
上的屬性。
class User { } User.prop = 1; User.prop // 1
1.5 靜態方法
可以在類裡面定義靜態方法,該方法不會被對象實例繼承,而是直接通過類來調用。
靜態方法裡使用this
是指向類。
class Utils { static printInfo() { this.info(); } static info() { console.log('hello'); } } Utils.printInfo() // hello
關於方法的調用范圍限制,比如:私有公有,ES6
暫時沒有提供,一般是通過約定,比如:在方法前面加下劃線_print()表示私有方法。
2、繼承
Java
中通過extends
實現類的繼承。ES6
中類也可以通過extends
實現繼承。
繼承時,子類必須在constructor
方法中調用super
方法,否則新建實例時會報錯。
class Point3D extends Point { constructor(x, y, z) { super(x, y); // 調用父類的constructor(x, y) this.z = z; } toString() { return super.toString() + ' ' + this.z ; // 調用父類的toString() } }
父類的靜態方法,也會被子類繼承
class Parent { static info() { console.log('hello world'); } } class Child extends Parent { } Child.info() // hello world
2.1 super關鍵字
在子類的構造函數必須執行一次super
函數,它代表瞭父類的構造函數。
class Parent {} class Child extends Parent { constructor() { super(); } }
在子類普通方法中通過super
調用父類的方法時,方法內部的this
指向當前的子類實例。
class Parent { constructor() { this.x = 1; this.y = 10 } printParent() { console.log(this.y); } print() { console.log(this.x); } } class Child extends Parent { constructor() { super(); this.x = 2; } m() { super.print(); } } let c = new Child(); c.printParent() // 10 c.m() // 2
2.2 _proto_和prototype
初學JavaScript
時, _proto_
和prototype
很容易混淆。首先我們知道每個JS
對象都會對應一個原型對象,並從原型對象繼承屬性和方法。
prototype
一些內置對象和函數的屬性,它是一個指針,指向一個對象,這個對象的用途就是包含所有實例共享的屬性和方法(我們把這個對象叫做原型對象)。_proto_
每個對象都有這個屬性,一般指向對應的構造函數的prototype
屬性。
下圖是一些擁有prototype內置對象:
根據上面描述,看下面代碼
var obj = {} // 等同於 var obj = new Object() // obj.__proto__指向Object構造函數的prototype obj.__proto__ === Object.prototype // true // obj.toString 調用方法從Object.prototype繼承 obj.toString === obj.__proto__.toString // true // 數組 var arr = [] arr.__proto__ === Array.prototype // true
對於function
對象,聲明的每個function
同時擁有prototype
和__proto__
屬性,創建的對象屬性__proto__
指向函數prototype
,函數的__proto__
又指向內置函數對象(Function)的prototype
。
function Foo(){} var f = new Foo(); f.__proto__ === Foo.prototype // true Foo.__proto__ === Function.prototype // true
2.3 繼承中的__proto__
類作為構造函數的語法糖,也會同時有prototype
屬性和__proto__
屬性,因此同時存在兩條繼承鏈。
- 子類的
__proto__
屬性,表示構造函數的繼承,總是指向父類。 - 子類prototype屬性的
__proto__
屬性,表示方法的繼承,總是指向父類的prototype
屬性。
class Parent { } class Child extends Parent { } Child.__proto__ === Parent // true Child.prototype.__proto__ === Parent.prototype // true
2.4 繼承實例中的__proto__
子類實例的__proto__
屬性,指向子類構造方法的prototype
。
子類實例的__proto__
屬性的__proto__
屬性,指向父類實例的__proto__
屬性。也就是說,子類的原型的原型,是父類的原型。
class Parent { } class Child extends Parent { } var p = new Parent(); var c = new Child(); c.__proto__ === p.__proto__ // false c.__proto__ === Child.prototype // true c.__proto__.__proto__ === p.__proto__ // true
3、小結
到此這篇關於前端JavaScript
中的class
類的文章就介紹到這瞭,更多相關JavaScript
中的class
內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 如何利用JavaScript 實現繼承
- Javascript 原型與原型鏈深入詳解
- 一文徹底理解js原生語法prototype,__proto__和constructor
- JavaScript三大重點同步異步與作用域和閉包及原型和原型鏈詳解
- JavaScript構造函數與原型之間的聯系