摘要:接下來我們看下類的寫法,這個就很接近于傳統面向對象語言了。如果你想了解傳統面向對象語言,這里是一個好切入點。作為對象時,指向父類的原型對象。這些就是為將來在中支持面向對象的類機制而預留的。
在ES5中,我們經常使用方法或者對象去模擬類的使用,并基于原型實現繼承,雖然可以實現功能,但是代碼并不優雅,很多人還是傾向于用 class 來組織代碼,很多類庫、框架創造了自己的 API 來實現 class 的功能。
ES6 時代終于有了 class (類)語法,能讓我們可以用更簡明的語法實現繼承,也使代碼的可讀性變得更高,同時為以后的JavaScript語言版本添加更多的面向對象特征打下基礎。有了ES6的class 以后媽媽再也不用擔心我們的代碼亂七八糟了,這簡直是喜大普奔的事情。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語法定義了一個Person類,該類有name和age兩個屬性和一個原型say方法。
這種寫法跟傳統的面向對象語言(比如 C++ 和 Java)差異很大。接下來我們看下ES6 類的寫法,這個就很接近于傳統面向對象語言了。如果你想了解傳統面向對象語言,這里是一個好切入點。
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歲"
上面代碼定義了一個同樣的Person類,constructor方法就是構造方法,而this關鍵字則代表實例對象,這更接近傳統語言的寫法。
注意:
雖然引入了class關鍵字,但ES6中并沒有真的引入類這個概念,通過class定義的仍然是函數:
console.log(typeof Person); // "function"
所以說,class僅僅是通過更簡單直觀的語法去實現原型鏈繼承。這種對語言功能沒有影響、但是給程序員帶來方便的新語法,被稱為語法糖。
二、類的傳參 constructor
在類的參數傳遞中我們用constructor( )進行傳參。傳遞參數后可以直接使用this.xxx進行調用。
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來傳遞參數,然后用了一個add方法,把參數相加。這和以前我們的函數傳參方法有些不一樣,所以小伙伴們要注意轉換下思維。
三、靜態方法
在面向對象語言中,靜態方法是指不需要實例化,可以通過類名直接調用的方法,但靜態方法不會繼承到類實例中,因此靜態方法經常用來作為工具函數。比如我們經常用的Math.random(),我們并不需要先new 一個Math然后再去用,一是如果作者那么設計JS一來是沒必要,二是用起來太繁瑣。
在使用函數模擬類時,可以像下面這樣定義靜態方法:
function Person(name, sex) {} Person.walk = function() { console.log("我會走路") } Person.walk(); // 我會走路 var person = new Person(); person.walk(); // TypeError
在ES6 class類定義中,可以使用static關鍵字定義:
class Person { constructor() {} static walk(){ console.log("我會走路") } } Person.walk(); // 我會走路 var person = new Person(); person.walk(); // TypeError
static關鍵字是ES6的另一個語法糖,static 使靜態方法聲明也成為了一個一等公民。
于此同時,靜態方法也是可以從子類中的super對象上調用的。
class Person { constructor() {} static walk(){ return "我會走路" } } class People extends Person { static walk() { return super.walk() + ", 我還會跑步"; } } People.walk(); //"我會走路, 我還會跑步"
四、封裝與繼承
封裝和繼承,是面向對象編程三大核心特征中非常重要的兩個,封裝和繼承在我們實際生活中也有非常多的應用。舉個例子,你去驢肉火燒店去吃飯。
老板把驢肉面和火燒一起買,起名字叫“精英驢火套餐”,這就是封裝。
而進去以后跟老板說,老板給我來個“82年的驢火套餐”這就是繼承。當然了你不僅僅能繼承,還能擴展自己的功能。比如你可以跟老板說,老板再給我加一個驢板腸。說的我都餓了,不過我們還是教編程的專欄,不是開店的專欄,我們繼續,看看ES6里面怎么玩繼承。
4.1 extends
舊的原型繼承有時看起來讓人非常頭疼。
function Child(firstName, lastName, age) { Parent.call(this, firstName, lastName) this.age = age } Child.prototype = Object.create(Parent.prototype) Child.constructor = Child
ES6中新的extends關鍵字解決了這個問題:
class Child extends Parent {}
上面代碼定義了一個Child類,該類通過extends關鍵字,繼承了Parent類的所有屬性和方法。
由于沒有在Child內部寫任何代碼,所以這兩個類完全一樣,等于復制了一個Parent類。
之后,我們在Child內部加上代碼:
class Child extends Parent { constructor(firstName, lastName, age) { super(firstName, lastName) // 調用父類的constructor(firstName, lastName) this.age = age } speak(){ return this.age + " " + super.speak(); // 調用父類的speak() } }
使用簡介的 extends 達到繼承的目的,而非雜亂的 Object.create()、.proto、Object.setPrototypeOf(),這樣能讓我們更順利的擴充功能。
4.2 super
super這個關鍵字,既可以當作函數使用,也可以當作對象使用。在這兩種情況下,它的用法完全不同。
(1)super作為函數調用
代表父類的構造函數,ES6中規定,子類的構造函數必須執行一次super函數。
class A {} class B extends A { constructor() { super(); } }
上面代碼中,子類B的構造函數之中的super(),代表調用父類的構造函數,這是必須的,否則 JavaScript 引擎會報錯。
注意,super雖然代表了父類A的構造函數,但是返回的是子類B的實例,即super內部的this指的是B,因此super()在這里相當于A.prototype.constructor.call(this)。
(2)super作為對象時,指向父類的原型對象。
class A { p() { return 2; } } class B extends A { constructor() { super(); console.log(super.p()); // 2 } } let b = new B();
與Java一樣,JavaScript也使用extends關鍵字實現繼承,子類中可以通過super關鍵字調用父類:
在 constructor 里面,super 的用法是 super()。它相當于一個函數,調用它等于調用父類的 constructor 。
但在普通方法里面,super 的用法是 super.prop 或者 super.method(),它相當于一個指向對象的 [[Prototype]] 的屬性。
4.3 getter(取值函數)、 setter(存值函數)
與 ES5 一樣,在“類”的內部可以使用get和set關鍵字,對某個屬性設置存值函數和取值函數,攔截該屬性的存取行為。
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"
五、總結
無論學什么知識,最重要也是最基礎的,要實現思想上的轉變,目前大部分框架和庫,都采用了面向對象方式編程。而且在工作中,要書寫中型和大型的項目也經常使用面向對象方式編程,可能大家習慣了面向過程方式編程,其實面向對象方式編程一旦習慣了,會讓我開發和思路更寬闊和易于開發項目。
從學習javascript基礎開始的時候,我們就了解了js中的保留字,js中并沒有用到,但是將來可能會用到的未來關鍵字。這些保留字中就包括:class、extends、super。這些就是為將來在js中支持面向對象的類機制而預留的。
果不其然,現在ES6語法中使用到了這些保留字,這些保留字成功升級成了關鍵字,可見當時javascript的設計者還是很有前瞻眼光的。
通過這些新的關鍵字,使類成為了JS中一個新的一等公民。但是目前為止,這些關于類的新關鍵字僅僅是建立在舊的原型系統上的語法糖。這樣做的原因是為了保證向后兼容性。也就是,舊代碼可以在不做任何hack的情況下,與新代碼同時運行。
不過,它使代碼的可讀性變得更高,并且為今后版本里更多面向對象的新特性打下了基礎。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/110234.html
摘要:基于原型的面向對象在基于原型的語言中如并不存在這種區別它只有對象不論是構造函數,實例,原型本身都是對象。允許動態地向單個的對象或者整個對象集中添加或移除屬性。為了解決以上兩個問題,提供了構造函數創建對象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認識面向對象 1. JavaScript...
摘要:基于原型的面向對象在基于原型的語言中如并不存在這種區別它只有對象不論是構造函數,實例,原型本身都是對象。允許動態地向單個的對象或者整個對象集中添加或移除屬性。為了解決以上兩個問題,提供了構造函數創建對象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認識面向對象 1. JavaScript...
摘要:除了以上介紹的幾種對象創建方式,此外還有寄生構造函數模式穩妥構造函數模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向對象 是以 對象 為中心的編程思想,它的思維方式是構造。 面向對象 編程的三大特點:封裝、繼承、多態: 封裝:屬性方法的抽象 繼承:一個類繼承(復制)另一個類的屬性/方法 多態:方...
摘要:熟悉面向對象編程的都知道,面向對象編程最重要的原則之一從外部接口劃分內部接口。所以,面向對象編程就類似于汽車一樣。 熟悉面向對象編程的都知道,面向對象編程最重要的原則之一 - 從外部接口劃分內部接口。也就是說,針對某一類事物,我們其實并不是那么在乎其內部究竟是怎樣去實現的,只關心怎樣使用而已。 為了理解這點,讓我們先來看看現實生活中的列子。通常,我們使用的設備非常復雜。但是從外部接口界...
閱讀 658·2021-11-23 09:51
閱讀 3258·2021-10-11 10:58
閱讀 15407·2021-09-29 09:47
閱讀 3528·2021-09-01 11:42
閱讀 1281·2019-08-29 16:43
閱讀 1832·2019-08-29 15:37
閱讀 2089·2019-08-29 12:56
閱讀 1718·2019-08-28 18:21