新聞中心
- 在面向?qū)ο蟮木幊讨?,class 是用于創(chuàng)建對(duì)象的可擴(kuò)展的程序代碼模版,它為對(duì)象提供了狀態(tài)(成員變量)的初始值和行為(成員函數(shù)或方法)的實(shí)現(xiàn)。
- Wikipedia
在日常開(kāi)發(fā)中,我們經(jīng)常需要?jiǎng)?chuàng)建許多相同類(lèi)型的對(duì)象,例如用戶(hù)(users)、商品(goods)或者任何其他東西。

成都創(chuàng)新互聯(lián)公司專(zhuān)注于企業(yè)網(wǎng)絡(luò)營(yíng)銷(xiāo)推廣、網(wǎng)站重做改版、易門(mén)網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5開(kāi)發(fā)、商城系統(tǒng)網(wǎng)站開(kāi)發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)營(yíng)銷(xiāo)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性?xún)r(jià)比高,為易門(mén)等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。
正如我們?cè)?構(gòu)造器和操作符 "new" 一章中已經(jīng)學(xué)到的,new function 可以幫助我們實(shí)現(xiàn)這種需求。
但在現(xiàn)代 JavaScript 中,還有一個(gè)更高級(jí)的“類(lèi)(class)”構(gòu)造方式,它引入許多非常棒的新功能,這些功能對(duì)于面向?qū)ο缶幊毯苡杏谩?/p>
一、“class” 語(yǔ)法
基本語(yǔ)法是:
- class MyClass {
- // class 方法
- constructor() { ... }
- method1() { ... }
- method2() { ... }
- method3() { ... }
- ...
- }
然后使用 new MyClass() 來(lái)創(chuàng)建具有上述列出的所有方法的新對(duì)象。
new 會(huì)自動(dòng)調(diào)用 constructor() 方法,因此我們可以在 constructor() 中初始化對(duì)象。
例如:
- class User {
- constructor(name) {
- this.name = name;
- }
- sayHi() {
- alert(this.name);
- }
- }
- // 用法:
- let user = new User("John");
- user.sayHi();
當(dāng) new User("John") 被調(diào)用:
- 一個(gè)新對(duì)象被創(chuàng)建。
- constructor 使用給定的參數(shù)運(yùn)行,并為其分配 this.name。
……然后我們就可以調(diào)用對(duì)象方法了,例如 user.sayHi。
類(lèi)的方法之間沒(méi)有逗號(hào)
對(duì)于新手開(kāi)發(fā)人員來(lái)說(shuō),常見(jiàn)的陷阱是在類(lèi)的方法之間放置逗號(hào),這會(huì)導(dǎo)致語(yǔ)法錯(cuò)誤。
不要把這里的符號(hào)與對(duì)象字面量相混淆。在類(lèi)中,不需要逗號(hào)。
二、什么是 class?
所以,class 到底是什么?正如人們可能認(rèn)為的那樣,這不是一個(gè)全新的語(yǔ)言級(jí)實(shí)體。
讓我們揭開(kāi)其神秘面紗,看看類(lèi)究竟是什么。這將有助于我們理解許多復(fù)雜的方面。
在 JavaScript 中,類(lèi)是一種函數(shù)。
看看下面這段代碼:
- class User {
- constructor(name) { this.name = name; }
- sayHi() { alert(this.name); }
- }
- // 佐證:User 是一個(gè)函數(shù)
- alert(typeof User); // function
class User {...} 構(gòu)造實(shí)際上做了如下的事兒:
- 創(chuàng)建一個(gè)名為 User 的函數(shù),該函數(shù)成為類(lèi)聲明的結(jié)果。該函數(shù)的代碼來(lái)自于 constructor 方法(如果我們不編寫(xiě)這種方法,那么它就被假定為空)。
- 存儲(chǔ)類(lèi)中的方法,例如 User.prototype 中的 sayHi。
當(dāng) new User 對(duì)象被創(chuàng)建后,當(dāng)我們調(diào)用其方法時(shí),它會(huì)從原型中獲取對(duì)應(yīng)的方法,正如我們?cè)?F.prototype 一章中所講的那樣。因此,對(duì)象 new User 可以訪(fǎng)問(wèn)類(lèi)中的方法。
我們可以將 class User 聲明的結(jié)果解釋為:
下面這些代碼很好地解釋了它們:
- class User {
- constructor(name) { this.name = name; }
- sayHi() { alert(this.name); }
- }
- // class 是一個(gè)函數(shù)
- alert(typeof User); // function
- // ...或者,更確切地說(shuō),是 constructor 方法
- alert(User === User.prototype.constructor); // true
- // 方法在 User.prototype 中,例如:
- alert(User.prototype.sayHi); // alert(this.name);
- // 在原型中實(shí)際上有兩個(gè)方法
- alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
三、不僅僅是語(yǔ)法糖
人們常說(shuō) class 是一個(gè)語(yǔ)法糖(旨在使內(nèi)容更易閱讀,但不引入任何新內(nèi)容的語(yǔ)法),因?yàn)槲覀儗?shí)際上可以在沒(méi)有 class 的情況下聲明相同的內(nèi)容:
- // 用純函數(shù)重寫(xiě) class User
- // 1. 創(chuàng)建構(gòu)造器函數(shù)
- function User(name) {
- this.name = name;
- }
- // 函數(shù)的原型(prototype)默認(rèn)具有 "constructor" 屬性,
- // 所以,我們不需要?jiǎng)?chuàng)建它
- // 2. 將方法添加到原型
- User.prototype.sayHi = function() {
- alert(this.name);
- };
- // 用法:
- let user = new User("John");
- user.sayHi();
這個(gè)定義的結(jié)果與使用類(lèi)得到的結(jié)果基本相同。因此,這確實(shí)是將 class 視為一種定義構(gòu)造器及其原型方法的語(yǔ)法糖的理由。
盡管,它們之間存在著重大差異:
- 首先,通過(guò) class 創(chuàng)建的函數(shù)具有特殊的內(nèi)部屬性標(biāo)記 [[FunctionKind]]:"classConstructor"。因此,它與手動(dòng)創(chuàng)建并不完全相同。編程語(yǔ)言會(huì)在許多地方檢查該屬性。例如,與普通函數(shù)不同,必須使用 new 來(lái)調(diào)用它:class User { constructor() {} } alert(typeof User); // function User(); // Error: Class constructor User cannot be invoked without 'new'此外,大多數(shù) JavaScript 引擎中的類(lèi)構(gòu)造器的字符串表示形式都以 “class…” 開(kāi)頭class User {constructor() {} } alert(User); // class User { ... }還有其他的不同之處,我們很快就會(huì)看到。
- 類(lèi)方法不可枚舉。 類(lèi)定義將 "prototype" 中的所有方法的 enumerable 標(biāo)志設(shè)置為 false。這很好,因?yàn)槿绻覀儗?duì)一個(gè)對(duì)象調(diào)用 for..in 方法,我們通常不希望 class 方法出現(xiàn)。
- 類(lèi)總是使用 use strict。 在類(lèi)構(gòu)造中的所有代碼都將自動(dòng)進(jìn)入嚴(yán)格模式。
此外,class 語(yǔ)法還帶來(lái)了許多其他功能,我們稍后將會(huì)探索它們。
四、類(lèi)表達(dá)式
就像函數(shù)一樣,類(lèi)可以在另外一個(gè)表達(dá)式中被定義,被傳遞,被返回,被賦值等。
這是一個(gè)類(lèi)表達(dá)式的例子:
- let User = class {
- sayHi() {
- alert("Hello");
- }
- };
類(lèi)似于命名函數(shù)表達(dá)式(Named Function Expressions),類(lèi)表達(dá)式可能也應(yīng)該有一個(gè)名字。
如果類(lèi)表達(dá)式有名字,那么該名字僅在類(lèi)內(nèi)部可見(jiàn):
- // “命名類(lèi)表達(dá)式(Named Class Expression)”
- // (規(guī)范中沒(méi)有這樣的術(shù)語(yǔ),但是它和命名函數(shù)表達(dá)式類(lèi)似)
- let User = class MyClass {
- sayHi() {
- alert(MyClass); // MyClass 這個(gè)名字僅在類(lèi)內(nèi)部可見(jiàn)
- }
- };
- new User().sayHi(); // 正常運(yùn)行,顯示 MyClass 中定義的內(nèi)容
- alert(MyClass); // error,MyClass 在外部不可見(jiàn)
我們甚至可以動(dòng)態(tài)地“按需”創(chuàng)建類(lèi),就像這樣:
- function makeClass(phrase) {
- // 聲明一個(gè)類(lèi)并返回它
- return class {
- sayHi() {
- alert(phrase);
- }
- };
- }
- // 創(chuàng)建一個(gè)新的類(lèi)
- let User = makeClass("Hello");
- new User().sayHi(); // Hello
五、Getters/setters
就像對(duì)象字面量,類(lèi)可能包括 getters/setters,計(jì)算屬性(computed properties)等。
這是一個(gè)使用 get/set 實(shí)現(xiàn) user.name 的示例:
- class User {
- constructor(name) {
- // 調(diào)用 setter
- this.name = name;
- }
- get name() {
- return this._name;
- }
- set name(value) {
- if (value.length < 4) {
- alert("Name is too short.");
- return;
- }
- this._name = value;
- }
- }
- let user = new User("John");
- alert(user.name); // John
- user = new User(""); // Name is too short.
從技術(shù)上來(lái)講,這樣的類(lèi)聲明可以通過(guò)在 User.prototype 中創(chuàng)建 getters 和 setters 來(lái)實(shí)現(xiàn)。
六、計(jì)算屬性名稱(chēng) […]
這里有一個(gè)使用中括號(hào) [...] 的計(jì)算方法名稱(chēng)示例:
- class User {
- ['say' + 'Hi']() {
- alert("Hello");
- }
- }
- new User().sayHi();
這種特性很容易記住,因?yàn)樗鼈兒蛯?duì)象字面量類(lèi)似。
七、Class 字段
舊的瀏覽器可能需要 polyfill
類(lèi)字段(field)是最近才添加到語(yǔ)言中的。
之前,我們的類(lèi)僅具有方法。
“類(lèi)字段”是一種允許添加任何屬性的語(yǔ)法。
例如,讓我們?cè)?class User 中添加一個(gè) name 屬性:
- class User {
- name = "John";
- sayHi() {
- alert(`Hello, ${this.name}!`);
- }
- }
- new User().sayHi(); // Hello, John!
所以,我們就只需在表達(dá)式中寫(xiě) " = ",就這樣。
類(lèi)字段重要的不同之處在于,它們會(huì)在每個(gè)獨(dú)立對(duì)象中被設(shè)好,而不是設(shè)在 User.prototype:
- class User {
- name = "John";
- }
- let user = new User();
- alert(user.name); // John
- alert(User.prototype.name); // undefined
我們也可以在賦值時(shí)使用更復(fù)雜的表達(dá)式和函數(shù)調(diào)用:
- class User {
- name = prompt("Name, please?", "John");
- }
- let user = new User();
- alert(user.name); // John
八、使用類(lèi)字段制作綁定方法
正如 函數(shù)綁定 一章中所講的,JavaScript 中的函數(shù)具有動(dòng)態(tài)的 this。它取決于調(diào)用上下文。
因此,如果一個(gè)對(duì)象方法被傳遞到某處,或者在另一個(gè)上下文中被調(diào)用,則 this 將不再是對(duì)其對(duì)象的引用。
例如,此代碼將顯示 undefined:
- class Button {
- constructor(value) {
- this.value = value;
- }
- click() {
- alert(this.value);
- }
- }
- let button = new Button("hello");
- setTimeout(button.click, 1000); // undefined
這個(gè)問(wèn)題被稱(chēng)為“丟失 this”。
我們?cè)?函數(shù)綁定 一章中講過(guò),有兩種可以修復(fù)它的方式:
- 傳遞一個(gè)包裝函數(shù),例如 setTimeout(() => button.click(), 1000)。
- 將方法綁定到對(duì)象,例如在 constructor 中。
類(lèi)字段提供了另一種非常優(yōu)雅的語(yǔ)法:
- class Button {
- constructor(value) {
- this.value = value;
- }
- click = () => {
- alert(this.value);
- }
- }
- let button = new Button("hello");
- setTimeout(button.click, 1000); // hello
類(lèi)字段 click = () => {...} 是基于每一個(gè)對(duì)象被創(chuàng)建的,在這里對(duì)于每一個(gè) Button 對(duì)象都有一個(gè)獨(dú)立的方法,在內(nèi)部都有一個(gè)指向此對(duì)象的 this。我們可以把 button.click 傳遞到任何地方,而且 this 的值總是正確的。
在瀏覽器環(huán)境中,它對(duì)于進(jìn)行事件監(jiān)聽(tīng)尤為有用。
九、總結(jié)
基本的類(lèi)語(yǔ)法看起來(lái)像這樣:
- class MyClass {
- prop = value; // 屬性
- constructor(...) { // 構(gòu)造器
- // ...
- }
- method(...) {} // method
- get something(...) {} // getter 方法
- set something(...) {} // setter 方法
- [Symbol.iterator]() {} // 有計(jì)算名稱(chēng)(computed name)的方法(此處為 symbol)
- // ...
- }
技術(shù)上來(lái)說(shuō),MyClass 是一個(gè)函數(shù)(我們提供作為 constructor 的那個(gè)),而 methods、getters 和 settors 都被寫(xiě)入了 MyClass.prototype。
文章題目:一篇帶給你JavaScript的Class語(yǔ)法介紹
網(wǎng)址分享:http://m.5511xx.com/article/cdodehi.html


咨詢(xún)
建站咨詢(xún)
