摘要:接下來我們看下類的寫法,這個(gè)就很接近于傳統(tǒng)面向?qū)ο笳Z言了。如果你想了解傳統(tǒng)面向?qū)ο笳Z言,這里是一個(gè)好切入點(diǎn)。作為對(duì)象時(shí),指向父類的原型對(duì)象。這些就是為將來在中支持面向?qū)ο蟮念悪C(jī)制而預(yù)留的。
在ES5中,我們經(jīng)常使用方法或者對(duì)象去模擬類的使用,并基于原型實(shí)現(xiàn)繼承,雖然可以實(shí)現(xiàn)功能,但是代碼并不優(yōu)雅,很多人還是傾向于用 class 來組織代碼,很多類庫、框架創(chuàng)造了自己的 API 來實(shí)現(xiàn) class 的功能。
ES6 時(shí)代終于有了 class (類)語法,能讓我們可以用更簡(jiǎn)明的語法實(shí)現(xiàn)繼承,也使代碼的可讀性變得更高,同時(shí)為以后的JavaScript語言版本添加更多的面向?qū)ο筇卣鞔蛳禄A(chǔ)。有了ES6的class 以后媽媽再也不用擔(dān)心我們的代碼亂七八糟了,這簡(jiǎn)直是喜大普奔的事情。ok,我們看看神奇的class.
一、 類的定義
1.1 ES5 模擬類定義
function Person( name , age ) { this.name = name; this.age = age; } Person.prototype.say = function(){ return "我叫" + this.name + ",今年" + this.age + "歲"; } var p = new Person("大彬哥",18); // Person?{name: "大彬哥", age: 18} p.say() //"我叫大彬哥,今年18歲"
使用ES5語法定義了一個(gè)Person類,該類有name和age兩個(gè)屬性和一個(gè)原型say方法。
這種寫法跟傳統(tǒng)的面向?qū)ο笳Z言(比如 C++ 和 Java)差異很大。接下來我們看下ES6 類的寫法,這個(gè)就很接近于傳統(tǒng)面向?qū)ο笳Z言了。如果你想了解傳統(tǒng)面向?qū)ο笳Z言,這里是一個(gè)好切入點(diǎn)。
1.2 ES6 class類定義
class Person { constructor( name , age ) { this.name = name; this.age = age; } say() { return "我叫" + this.name + ",今年" + this.age + "歲"; } } var p = new Person("大彬哥",18); // Person?{name: "大彬哥", age: 18} p.say() //"我叫大彬哥,今年18歲"
上面代碼定義了一個(gè)同樣的Person類,constructor方法就是構(gòu)造方法,而this關(guān)鍵字則代表實(shí)例對(duì)象,這更接近傳統(tǒng)語言的寫法。
注意:
雖然引入了class關(guān)鍵字,但ES6中并沒有真的引入類這個(gè)概念,通過class定義的仍然是函數(shù):
console.log(typeof Person); // "function"
所以說,class僅僅是通過更簡(jiǎn)單直觀的語法去實(shí)現(xiàn)原型鏈繼承。這種對(duì)語言功能沒有影響、但是給程序員帶來方便的新語法,被稱為語法糖。
二、類的傳參 constructor
在類的參數(shù)傳遞中我們用constructor( )進(jìn)行傳參。傳遞參數(shù)后可以直接使用this.xxx進(jìn)行調(diào)用。
class Person { constructor(a,b){ this.a=a; this.b=b; } add(){ return this.a + this.b; } } let p = new Person(18,30); console.log(p.add()); // 48 (18+30)
我們用constructor來傳遞參數(shù),然后用了一個(gè)add方法,把參數(shù)相加。這和以前我們的函數(shù)傳參方法有些不一樣,所以小伙伴們要注意轉(zhuǎn)換下思維。
三、靜態(tài)方法
在面向?qū)ο笳Z言中,靜態(tài)方法是指不需要實(shí)例化,可以通過類名直接調(diào)用的方法,但靜態(tài)方法不會(huì)繼承到類實(shí)例中,因此靜態(tài)方法經(jīng)常用來作為工具函數(shù)。比如我們經(jīng)常用的Math.random(),我們并不需要先new 一個(gè)Math然后再去用,一是如果作者那么設(shè)計(jì)JS一來是沒必要,二是用起來太繁瑣。
在使用函數(shù)模擬類時(shí),可以像下面這樣定義靜態(tài)方法:
function Person(name, sex) {} Person.walk = function() { console.log("我會(huì)走路") } Person.walk(); // 我會(huì)走路 var person = new Person(); person.walk(); // TypeError
在ES6 class類定義中,可以使用static關(guān)鍵字定義:
class Person { constructor() {} static walk(){ console.log("我會(huì)走路") } } Person.walk(); // 我會(huì)走路 var person = new Person(); person.walk(); // TypeError
static關(guān)鍵字是ES6的另一個(gè)語法糖,static 使靜態(tài)方法聲明也成為了一個(gè)一等公民。
于此同時(shí),靜態(tài)方法也是可以從子類中的super對(duì)象上調(diào)用的。
class Person { constructor() {} static walk(){ return "我會(huì)走路" } } class People extends Person { static walk() { return super.walk() + ", 我還會(huì)跑步"; } } People.walk(); //"我會(huì)走路, 我還會(huì)跑步"
四、封裝與繼承
封裝和繼承,是面向?qū)ο缶幊倘蠛诵奶卣髦蟹浅V匾膬蓚€(gè),封裝和繼承在我們實(shí)際生活中也有非常多的應(yīng)用。舉個(gè)例子,你去驢肉火燒店去吃飯。
老板把驢肉面和火燒一起買,起名字叫“精英驢火套餐”,這就是封裝。
而進(jìn)去以后跟老板說,老板給我來個(gè)“82年的驢火套餐”這就是繼承。當(dāng)然了你不僅僅能繼承,還能擴(kuò)展自己的功能。比如你可以跟老板說,老板再給我加一個(gè)驢板腸。說的我都餓了,不過我們還是教編程的專欄,不是開店的專欄,我們繼續(xù),看看ES6里面怎么玩繼承。
4.1 extends
舊的原型繼承有時(shí)看起來讓人非常頭疼。
function Child(firstName, lastName, age) { Parent.call(this, firstName, lastName) this.age = age } Child.prototype = Object.create(Parent.prototype) Child.constructor = Child
ES6中新的extends關(guān)鍵字解決了這個(gè)問題:
class Child extends Parent {}
上面代碼定義了一個(gè)Child類,該類通過extends關(guān)鍵字,繼承了Parent類的所有屬性和方法。
由于沒有在Child內(nèi)部寫任何代碼,所以這兩個(gè)類完全一樣,等于復(fù)制了一個(gè)Parent類。
之后,我們?cè)贑hild內(nèi)部加上代碼:
class Child extends Parent { constructor(firstName, lastName, age) { super(firstName, lastName) // 調(diào)用父類的constructor(firstName, lastName) this.age = age } speak(){ return this.age + " " + super.speak(); // 調(diào)用父類的speak() } }
使用簡(jiǎn)介的 extends 達(dá)到繼承的目的,而非雜亂的 Object.create()、.proto、Object.setPrototypeOf(),這樣能讓我們更順利的擴(kuò)充功能。
4.2 super
super這個(gè)關(guān)鍵字,既可以當(dāng)作函數(shù)使用,也可以當(dāng)作對(duì)象使用。在這兩種情況下,它的用法完全不同。
(1)super作為函數(shù)調(diào)用
代表父類的構(gòu)造函數(shù),ES6中規(guī)定,子類的構(gòu)造函數(shù)必須執(zhí)行一次super函數(shù)。
class A {} class B extends A { constructor() { super(); } }
上面代碼中,子類B的構(gòu)造函數(shù)之中的super(),代表調(diào)用父類的構(gòu)造函數(shù),這是必須的,否則 JavaScript 引擎會(huì)報(bào)錯(cuò)。
注意,super雖然代表了父類A的構(gòu)造函數(shù),但是返回的是子類B的實(shí)例,即super內(nèi)部的this指的是B,因此super()在這里相當(dāng)于A.prototype.constructor.call(this)。
(2)super作為對(duì)象時(shí),指向父類的原型對(duì)象。
class A { p() { return 2; } } class B extends A { constructor() { super(); console.log(super.p()); // 2 } } let b = new B();
與Java一樣,JavaScript也使用extends關(guān)鍵字實(shí)現(xiàn)繼承,子類中可以通過super關(guān)鍵字調(diào)用父類:
在 constructor 里面,super 的用法是 super()。它相當(dāng)于一個(gè)函數(shù),調(diào)用它等于調(diào)用父類的 constructor 。
但在普通方法里面,super 的用法是 super.prop 或者 super.method(),它相當(dāng)于一個(gè)指向?qū)ο蟮?[[Prototype]] 的屬性。
4.3 getter(取值函數(shù))、 setter(存值函數(shù))
與 ES5 一樣,在“類”的內(nèi)部可以使用get和set關(guān)鍵字,對(duì)某個(gè)屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為。
class Person { constructor() {} get prop() { return "getter"; } set prop(value) { console.log("setter: "+value); } } let p = new Person(); p.prop = 666; // setter: 666 p.prop // "getter"
五、總結(jié)
無論學(xué)什么知識(shí),最重要也是最基礎(chǔ)的,要實(shí)現(xiàn)思想上的轉(zhuǎn)變,目前大部分框架和庫,都采用了面向?qū)ο蠓绞骄幊獭6以诠ぷ髦?,要書寫中型和大型的?xiàng)目也經(jīng)常使用面向?qū)ο蠓绞骄幊?,可能大家?xí)慣了面向過程方式編程,其實(shí)面向?qū)ο蠓绞骄幊桃坏┝?xí)慣了,會(huì)讓我開發(fā)和思路更寬闊和易于開發(fā)項(xiàng)目。
從學(xué)習(xí)javascript基礎(chǔ)開始的時(shí)候,我們就了解了js中的保留字,js中并沒有用到,但是將來可能會(huì)用到的未來關(guān)鍵字。這些保留字中就包括:class、extends、super。這些就是為將來在js中支持面向?qū)ο蟮念悪C(jī)制而預(yù)留的。
果不其然,現(xiàn)在ES6語法中使用到了這些保留字,這些保留字成功升級(jí)成了關(guān)鍵字,可見當(dāng)時(shí)javascript的設(shè)計(jì)者還是很有前瞻眼光的。
通過這些新的關(guān)鍵字,使類成為了JS中一個(gè)新的一等公民。但是目前為止,這些關(guān)于類的新關(guān)鍵字僅僅是建立在舊的原型系統(tǒng)上的語法糖。這樣做的原因是為了保證向后兼容性。也就是,舊代碼可以在不做任何hack的情況下,與新代碼同時(shí)運(yùn)行。
不過,它使代碼的可讀性變得更高,并且為今后版本里更多面向?qū)ο蟮男绿匦源蛳铝嘶A(chǔ)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/110234.html
摘要:基于原型的面向?qū)ο笤诨谠偷恼Z言中如并不存在這種區(qū)別它只有對(duì)象不論是構(gòu)造函數(shù),實(shí)例,原型本身都是對(duì)象。允許動(dòng)態(tài)地向單個(gè)的對(duì)象或者整個(gè)對(duì)象集中添加或移除屬性。為了解決以上兩個(gè)問題,提供了構(gòu)造函數(shù)創(chuàng)建對(duì)象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認(rèn)識(shí)面向?qū)ο?1. JavaScript...
摘要:基于原型的面向?qū)ο笤诨谠偷恼Z言中如并不存在這種區(qū)別它只有對(duì)象不論是構(gòu)造函數(shù),實(shí)例,原型本身都是對(duì)象。允許動(dòng)態(tài)地向單個(gè)的對(duì)象或者整個(gè)對(duì)象集中添加或移除屬性。為了解決以上兩個(gè)問題,提供了構(gòu)造函數(shù)創(chuàng)建對(duì)象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認(rèn)識(shí)面向?qū)ο?1. JavaScript...
摘要:除了以上介紹的幾種對(duì)象創(chuàng)建方式,此外還有寄生構(gòu)造函數(shù)模式穩(wěn)妥構(gòu)造函數(shù)模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向?qū)ο?是以 對(duì)象 為中心的編程思想,它的思維方式是構(gòu)造。 面向?qū)ο?編程的三大特點(diǎn):封裝、繼承、多態(tài): 封裝:屬性方法的抽象 繼承:一個(gè)類繼承(復(fù)制)另一個(gè)類的屬性/方法 多態(tài):方...
摘要:熟悉面向?qū)ο缶幊痰亩贾?,面向?qū)ο缶幊套钪匾脑瓌t之一從外部接口劃分內(nèi)部接口。所以,面向?qū)ο缶幊叹皖愃朴谄囈粯印? 熟悉面向?qū)ο缶幊痰亩贾?,面向?qū)ο缶幊套钪匾脑瓌t之一 - 從外部接口劃分內(nèi)部接口。也就是說,針對(duì)某一類事物,我們其實(shí)并不是那么在乎其內(nèi)部究竟是怎樣去實(shí)現(xiàn)的,只關(guān)心怎樣使用而已。 為了理解這點(diǎn),讓我們先來看看現(xiàn)實(shí)生活中的列子。通常,我們使用的設(shè)備非常復(fù)雜。但是從外部接口界...
閱讀 666·2021-11-23 09:51
閱讀 3314·2021-10-11 10:58
閱讀 15488·2021-09-29 09:47
閱讀 3580·2021-09-01 11:42
閱讀 1297·2019-08-29 16:43
閱讀 1840·2019-08-29 15:37
閱讀 2121·2019-08-29 12:56
閱讀 1732·2019-08-28 18:21