class的基本用法《第三章》

01.靜態(tài)方法

類相當(dāng)于實例的原型颜说,所有在類中定義的方法儒将,都會被實例繼承闹丐。如果在一個方法前鹦付,加上static關(guān)鍵字,就表示該方法不會被實例繼承蝗蛙,而是直接通過類來調(diào)用蝇庭,這就稱為“靜態(tài)方法”。

class Foo{

static classMethod() {

return 'hello';

? }

}

Foo.classMethod()// 'hello'

varfoo=new Foo();

foo.classMethod()

// TypeError: foo.classMethod is not a function

上面代碼中捡硅,F(xiàn)oo類的classMethod方法前有static關(guān)鍵字哮内,表明該方法是一個靜態(tài)方法,可以直接在Foo類上調(diào)用(Foo.classMethod()),而不是在Foo類的實例上調(diào)用北发。如果在實例上調(diào)用靜態(tài)方法纹因,會拋出一個錯誤,表示不存在該方法琳拨。

注意瞭恰,如果靜態(tài)方法包含this關(guān)鍵字,這個this指的是類狱庇,而不是實例惊畏。

class Foo{

static bar() {

this.baz();

? }

static baz() {

console.log('hello');

? }

baz() {

console.log('world');

? }

}

Foo.bar()// hello

上面代碼中,靜態(tài)方法bar調(diào)用了this.baz密任,這里的this指的是Foo類颜启,而不是Foo的實例,等同于調(diào)用Foo.baz浪讳。另外缰盏,從這個例子還可以看出,靜態(tài)方法可以與非靜態(tài)方法重名淹遵。

父類的靜態(tài)方法口猜,可以被子類繼承。

class Foo{

static classMethod() {

return'hello';

? }

}

class Barextends Foo{

}

Bar.classMethod()// 'hello'

上面代碼中合呐,父類Foo有一個靜態(tài)方法暮的,子類Bar可以調(diào)用這個方法笙以。

靜態(tài)方法也是可以從super對象上調(diào)用的淌实。

class Foo{

static classMethod() {

return'hello';

? }

}

class Bar extends Foo{

static classMethod() {

returnsuper.classMethod() + ', too';

? }

}

Bar.classMethod()// "hello, too"

2.屬性的新寫法

實例屬性除了定義在constructor()方法里面的this上面,也可以定義在類的最頂層猖腕。

class IncreasingCounter{

constructor() {

this._count = 0;

? }

get value()? {

console.log('Getting the current value!');

returnthis._count;

? }

increment() {

this._count++;

? }

}

上面代碼中拆祈,實例屬性this._count定義在constructor()方法里面。另一種寫法是倘感,這個屬性也可以定義在類的最頂層放坏,其他都不變。

class IncreasingCounter{

_count=0;

get value() {

console.log('Getting the current value!');

return this._count;

? }

increment() {

this._count++;

? }

}

上面代碼中老玛,實例屬性_count與取值函數(shù)value()和increment()方法淤年,處于同一個層級。這時蜡豹,不需要在實例屬性前面加上this麸粮。

這種新寫法的好處是,所有實例對象自身的屬性都定義在類的頭部镜廉,看上去比較整齊弄诲,一眼就能看出這個類有哪些實例屬性。

class foo{

bar = 'hello';

baz = 'world';

constructor() {

// ...

? }

}

上面的代碼娇唯,一眼就能看出齐遵,foo類有兩個實例屬性寂玲,一目了然。另外梗摇,寫起來也比較簡潔拓哟。

3.靜態(tài)屬性

靜態(tài)屬性指的是 Class 本身的屬性,即Class.propName伶授,而不是定義在實例對象(this)上的屬性彰檬。

class Foo{

}

Foo.prop=1;

Foo.prop// 1

上面的寫法為Foo類定義了一個靜態(tài)屬性prop。

目前谎砾,只有這種寫法可行逢倍,因為 ES6 明確規(guī)定,Class 內(nèi)部只有靜態(tài)方法景图,沒有靜態(tài)屬性〗系瘢現(xiàn)在有一個提案提供了類的靜態(tài)屬性,寫法是在實例屬性的前面挚币,加上static關(guān)鍵字亮蒋。

class MyClass{

static myStaticProp = 42;

constructor() {

console.log(MyClass.myStaticProp);// 42

? }

}

這個新寫法大大方便了靜態(tài)屬性的表達。

// 老寫法

class Foo{

// ...

}

Foo.prop = 1;

// 新寫法

class Foo{

staticprop = 1;

}

上面代碼中妆毕,老寫法的靜態(tài)屬性定義在類的外部慎玖。整個類生成以后,再生成靜態(tài)屬性笛粘。這樣讓人很容易忽略這個靜態(tài)屬性趁怔,也不符合相關(guān)代碼應(yīng)該放在一起的代碼組織原則。另外薪前,新寫法是顯式聲明(declarative)润努,而不是賦值處理,語義更好示括。

4.私有方法和私有屬性

現(xiàn)有的解決方案

私有方法和私有屬性铺浇,是只能在類的內(nèi)部訪問的方法和屬性,外部不能訪問垛膝。這是常見需求鳍侣,有利于代碼的封裝,但 ES6 不提供吼拥,只能通過變通方法模擬實現(xiàn)倚聚。

一種做法是在命名上加以區(qū)別。

class Widget{

// 公有方法

foo(baz) {

this._bar(baz);

? }

// 私有方法

_bar(baz) {

returnthis.snaf=baz;

? }

// ...

}

上面代碼中扔罪,_bar()方法前面的下劃線秉沼,表示這是一個只限于內(nèi)部使用的私有方法。但是,這種命名是不保險的唬复,在類的外部矗积,還是可以調(diào)用到這個方法。

另一種方法就是索性將私有方法移出類敞咧,因為類內(nèi)部的所有方法都是對外可見的棘捣。

class Widget{

foo(baz) {

bar.call(this,baz);

? }

// ...

}

function bar(baz) {

return this.snaf=baz;

}

上面代碼中,foo是公開方法休建,內(nèi)部調(diào)用了bar.call(this, baz)乍恐。這使得bar()實際上成為了當(dāng)前類的私有方法。

還有一種方法是利用Symbol值的唯一性测砂,將私有方法的名字命名為一個Symbol值茵烈。

const bar = Symbol('bar');

const snaf = Symbol('snaf');

export default class myClass{

// 公有方法

foo(baz) {

this[bar](baz);

? }

// 私有方法

[bar](baz) {

returnthis[snaf] = baz;

? }

// ...

};

上面代碼中,bar和snaf都是Symbol值砌些,一般情況下無法獲取到它們呜投,因此達到了私有方法和私有屬性的效果。但是也不是絕對不行存璃,Reflect.ownKeys()依然可以拿到它們仑荐。

const inst = newmyClass();

Reflect.ownKeys(myClass.prototype)

// [ 'constructor', 'foo', Symbol(bar) ]

上面代碼中,Symbol 值的屬性名依然可以從類的外部拿到纵东。

私有屬性的提案

目前粘招,有一個提案,為class加了私有屬性偎球。方法是在屬性名之前洒扎,使用#表示。

class IncreasingCounter{

#count = 0;

get value() {

console.log('Getting the current value!');

return this.#count;

? }

increment() {

this.#count++;

? }

}

上面代碼中甜橱,#count就是私有屬性逊笆,只能在類的內(nèi)部使用(this.#count)栈戳。如果在類的外部使用岂傲,就會報錯。

const counter = new IncreasingCounter();

counter.#count // 報錯

counter.#count = 42 // 報錯

上面代碼在類的外部子檀,讀取私有屬性镊掖,就會報錯。

下面是另一個例子褂痰。

class Point{

#x;

constructor(x=0) {

this.#x = +x;

? }

get x() {

returnthis.#x;

? }

set x(value) {

this.#x = +value;

? }

}

上面代碼中亩进,#x就是私有屬性,在Point類之外是讀取不到這個屬性的缩歪。由于井號#是屬性名的一部分归薛,使用時必須帶有#一起使用,所以#x和x是兩個不同的屬性。

之所以要引入一個新的前綴#表示私有屬性主籍,而沒有采用private關(guān)鍵字习贫,是因為 JavaScript 是一門動態(tài)語言,沒有類型聲明千元,使用獨立的符號似乎是唯一的比較方便可靠的方法苫昌,能夠準(zhǔn)確地區(qū)分一種屬性是否為私有屬性。另外幸海,Ruby 語言使用@表示私有屬性祟身,ES6 沒有用這個符號而使用#,是因為@已經(jīng)被留給了 Decorator物独。

這種寫法不僅可以寫私有屬性袜硫,還可以用來寫私有方法。

class Foo{

#a;

#b;

const ructor(a,b) {

this.#a = a;

this.#b = b;

? }

#sum() {

return this.#a + this.#b;

? }

printSum() {

console.log(this.#sum());

? }

}

上面代碼中挡篓,#sum()就是一個私有方法父款。

另外,私有屬性也可以設(shè)置 getter 和 setter 方法瞻凤。

class Counter{

#xValue = 0;

const ructor() {

super();

// ...

? }

get #x() { return #xValue; }

set #x(value) {

this.#xValue = value;

? }

}

上面代碼中憨攒,#x是一個私有屬性,它的讀寫都通過get #x()和set #x()來完成阀参。

私有屬性不限于從this引用肝集,只要是在類的內(nèi)部,實例也可以引用私有屬性蛛壳。

class Foo{

#privateValue = 42;

staticgetPrivateValue(foo) {

return foo.#privateValue;

? }

}

Foo.getPrivateValue(newFoo());// 42

上面代碼允許從實例foo上面引用私有屬性杏瞻。

私有屬性和私有方法前面,也可以加上static關(guān)鍵字衙荐,表示這是一個靜態(tài)的私有屬性或私有方法捞挥。

class FakeMath{

staticPI = 22/7;

static #totallyRandomNumber = 4;

static #computeRandomNumber() {

return FakeMath.#totallyRandomNumber;

? }

staticrandom() {

console.log('I heard you like random numbers…')

returnFakeMath.#computeRandomNumber();

? }

}

FakeMath.PI// 3.142857142857143

FakeMath.random()

// I heard you like random numbers…

// 4

FakeMath.#totallyRandomNumber // 報錯

FakeMath.#computeRandomNumber() // 報錯

上面代碼中,#totallyRandomNumber是私有屬性忧吟,#computeRandomNumber()是私有方法砌函,只能在FakeMath這個類的內(nèi)部調(diào)用,外部調(diào)用就會報錯溜族。

in 運算符

try...catch結(jié)構(gòu)可以用來判斷是否存在某個私有屬性讹俊。

class A{

use(obj) {

try{

obj.#foo;

}catch{

// 私有屬性 #foo 不存在

?? }

? }

}

consta=newA();

a.use(a);// 報錯

上面示例中,類A并不存在私有屬性#foo煌抒,所以try...catch報錯了仍劈。

這樣的寫法很麻煩,可讀性很差寡壮,V8 引擎改進了in運算符贩疙,使它也可以用來判斷私有屬性讹弯。

class A{

use(obj) {

if(#foo in obj) {

// 私有屬性 #foo 存在

}else{

// 私有屬性 #foo 不存在

?? }

? }

}

上面示例中,in運算符判斷當(dāng)前類A的實例这溅,是否有私有屬性#foo闸婴,如果有返回true,否則返回false芍躏。

in也可以跟this一起配合使用邪乍。

class A{

#foo = 0;

m() {

console.log(#foo in this); // true

console.log(#bar in this); // false

? }

}

注意,判斷私有屬性時对竣,in只能用在定義該私有屬性的類的內(nèi)部庇楞。

class A{

#foo = 0;

statictest(obj) {

console.log(#foo in obj);

? }

}

A.test(newA())// true

A.test({})// false

class B{

#foo = 0;

}

A.test(newB())// false

上面示例中,類A的私有屬性#foo否纬,只能在類A內(nèi)部使用in運算符判斷吕晌,而且只對A的實例返回true,對于其他對象都返回false临燃。

子類從父類繼承的私有屬性睛驳,也可以使用in運算符來判斷。

class A{

#foo = 0;

statictest(obj) {

console.log(#foo in obj);

? }

}

classSubAextendA{};

A.test(newSubA())// true

上面示例中膜廊,SubA從父類繼承了私有屬性#foo乏沸,in運算符也有效。

注意爪瓜,in運算符對于Object.create()蹬跃、Object.setPrototypeOf形成的繼承,是無效的铆铆,因為這種繼承不會傳遞私有屬性蝶缀。

class A{

#foo = 0;

statictest(obj) {

console.log(#foo in obj);

? }

}

consta= new A();

consto1=Object.create(a);

A.test(o1)// false

A.test(o1.__proto__)// true

const o2={};

Object.setPrototypeOf(o2,A);

A.test(o2)// false

A.test(o2.__proto__)// true

上面示例中,對于修改原型鏈形成的繼承薄货,子類都取不到父類的私有屬性翁都,所以in運算符無效。

5.new.target 屬性

new是從構(gòu)造函數(shù)生成實例對象的命令谅猾。ES6 為new命令引入了一個new.target屬性柄慰,該屬性一般用在構(gòu)造函數(shù)之中,返回new命令作用于的那個構(gòu)造函數(shù)赊瞬。如果構(gòu)造函數(shù)不是通過new命令或Reflect.construct()調(diào)用的先煎,new.target會返回undefined,因此這個屬性可以用來確定構(gòu)造函數(shù)是怎么調(diào)用的巧涧。

function Person(name) {

if(new.target!==undefined) {

this.name=name;

}else{

thrownewError('必須使用 new 命令生成實例');

? }

}

// 另一種寫法

function Person(name) {

if(new.target===Person) {

this.name=name;

}else{

thrownewError('必須使用 new 命令生成實例');

? }

}

varperson=newPerson('張三');// 正確

varnotAPerson=Person.call(person,'張三');// 報錯

上面代碼確保構(gòu)造函數(shù)只能通過new命令調(diào)用。

Class 內(nèi)部調(diào)用new.target遥倦,返回當(dāng)前 Class谤绳。

class Rectangle{

const ructor(length,width) {

console.log(new.target===Rectangle);

this.length=length;

this.width=width;

? }

}

varobj=newRectangle(3,4);// 輸出 true

需要注意的是占锯,子類繼承父類時,new.target會返回子類缩筛。

class Rectangle{

const ructor(length,width) {

console.log(new.target===Rectangle);

// ...

? }

}

class SquareextendsRectangle{

const ructor(length,width) {

super(length,width);

? }

}

varobj=newSquare(3);// 輸出 false

上面代碼中消略,new.target會返回子類。

利用這個特點瞎抛,可以寫出不能獨立使用艺演、必須繼承后才能使用的類。

class Shape{

const ructor() {

if(new.target===Shape) {

thrownewError('本類不能實例化');

?? }

? }

}

class RectangleextendsShape{

const ructor(length,width) {

super();

// ...

? }

}

varx=newShape();// 報錯

vary=newRectangle(3,4);// 正確

上面代碼中桐臊,Shape類不能被實例化胎撤,只能用于繼承。

注意断凶,在函數(shù)外部伤提,使用new.target會報錯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末认烁,一起剝皮案震驚了整個濱河市肿男,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌却嗡,老刑警劉巖舶沛,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異窗价,居然都是意外死亡冠王,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門舌镶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柱彻,“玉大人,你說我怎么就攤上這事餐胀∮纯” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵否灾,是天一觀的道長卖擅。 經(jīng)常有香客問我,道長墨技,這世上最難降的妖魔是什么惩阶? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮扣汪,結(jié)果婚禮上断楷,老公的妹妹穿的比我還像新娘。我一直安慰自己崭别,他們只是感情好冬筒,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布恐锣。 她就那樣靜靜地躺著,像睡著了一般舞痰。 火紅的嫁衣襯著肌膚如雪土榴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天响牛,我揣著相機與錄音玷禽,去河邊找鬼。 笑死呀打,一個胖子當(dāng)著我的面吹牛矢赁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播聚磺,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼坯台,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瘫寝?” 一聲冷哼從身側(cè)響起蜒蕾,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎焕阿,沒想到半個月后咪啡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡暮屡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年撤摸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褒纲。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡准夷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出莺掠,到底是詐尸還是另有隱情衫嵌,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布彻秆,位于F島的核電站楔绞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏唇兑。R本人自食惡果不足惜酒朵,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扎附。 院中可真熱鬧蔫耽,春花似錦、人聲如沸帕棉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽香伴。三九已至慰枕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間即纲,已是汗流浹背具帮。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留低斋,地道東北人蜂厅。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像膊畴,于是被迫代替她去往敵國和親掘猿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持唇跨,譯者再次奉上一點點福利:阿里云產(chǎn)品券稠通,享受所有官網(wǎng)優(yōu)惠,并抽取幸運大...
    HetfieldJoe閱讀 3,655評論 2 27
  • 簡介 類的由來 JavaScript 語言中买猖,生成實例對象的傳統(tǒng)方法是通過構(gòu)造函數(shù)改橘。下面是一個例子。 上面這種寫法...
    硅谷干貨閱讀 273評論 0 0
  • Class 的基本語法 簡介 JavaScript 語言中玉控,生成實例對象的傳統(tǒng)方法是通過構(gòu)造函數(shù)飞主。下面是一個例子。...
    huilegezai閱讀 520評論 0 0
  • 僅為方便個人查詢使用來源:http://es6.ruanyifeng.com/#docs/class 簡介 類的由...
    zqyadam閱讀 156評論 0 0
  • 簡介 JavaScript語言中高诺,生成實例對象的傳統(tǒng)方法是通過構(gòu)造函數(shù)碌识。 ES6引入了Class(類)這個概念,作...
    oWSQo閱讀 367評論 0 0