国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

對Javascript 類、原型鏈、繼承的理解

sevi_stuo / 714人閱讀

摘要:因為我們用這個函數來構造對象,所以我們也把稱作構造函數。所以通過定義構造函數,就相當于定義了一個類,通過關鍵字,即可生成一個實例化的對象。

一、序言

??和其他面向對象的語言(如Java)不同,Javascript語言對類的實現和繼承的實現沒有標準的定義,而是將這些交給了程序員,讓程序員更加靈活地(當然剛開始也更加頭疼)去定義類,實現繼承。(以下不討論ES6中利用class、extends關鍵字來實現類和繼承;實質上,ES6中的class、extends關鍵字是利用語法糖實現的)

</>復制代碼

  1. Javascript靈活到甚至可以實現接口的封裝(類似Java中的Interfaceimplements)。
二、類的實現

1.我對類的理解
首先,我先說說我對類的理解:類是包含了一系列【屬性/方法】的集合,可以通過類的構造函數創建一個實例對象(例如人類是一個類,而每一個人就是一個實例對象),而這個實例對象中會包含兩方面內容:
a.類的所有非靜態【屬性/方法】
非靜態【屬性/方法】就是每一個實例所特有的,屬于個性。(例如每個人的名字都不相同,而名字這個屬性就是一個非靜態屬性)
b.類的所有靜態【屬性/方法】
靜態【屬性/方法】就是每一個實例所共享的,屬于共性。(例如每個人都要吃飯,而吃飯這個方法就是一個非靜態方法)
2.Javascript對類的實現
a.利用函數創建類,利用new關鍵字生成實例對象
(話不多說,先上代碼,以下沒有特別說明的話,我都會先上代碼,然后進行解釋說明)

</>復制代碼

  1. // 代碼2.2.a
  2. function Human() {
  3. console.log("create human here")
  4. }
  5. var fakeperson = Human() // undefined
  6. var person = new Human() // {}

這里Human既是一個普通函數,也是一個類的構造函數,當調用Human()的時候,它作為一個普通函數會被執行,會輸出create human here,但是沒有返回值(即返回undefined);而當調用new Human()時,也會輸出create human here并且返回一個對象。因為我們用Human這個函數來構造對象,所以我們也把Human稱作構造函數。所以通過定義構造函數,就相當于定義了一個類,通過new關鍵字,即可生成一個實例化的對象。

b.利用構造函數實現非靜態【屬性/方法】

</>復制代碼

  1. // 代碼2.2.b
  2. function Human(name) {
  3. this.name = name
  4. }
  5. var person_1 = new Human("Jack")
  6. var person_2 = new Human("Rose")
  7. console.log(person_1.name) // Jack
  8. console.log(person_2.name) // Rose

這里的Human構造函數中多了一個參數并且函數體中多了一句this.name = name,這句話的中的this指針指向new關鍵字返回的實例化對象,所以根據構造函數參數的不同,其生成的對象中的具有的屬性name的值也會不同。而這里的name就是這個類的 非靜態【屬性/方法】

c.利用prototype實現靜態【屬性/方法】
這里因為要用到原型鏈的知識,所以放到原型鏈后面說。

三、原型鏈

1.類的prototype是什么?
在Javascript中,每當我們定義一個構造函數,Javascript引擎就會自動為這個類中添加一個prototype(也被稱作原型)
2.對象的__proto__是什么?
在Javascript中,每當我們使用new創建一個對象時,Javascript引擎就會自動為這個對象中添加一個__proto__屬性,并讓其指向其類的prototype

</>復制代碼

  1. // 代碼3.2
  2. function Human(name) {
  3. this.name = name
  4. }
  5. console.log(Human.prototype)
  6. var person_test1 = new Human("Test1")
  7. var person_test2 = new Human("Test2")
  8. console.log(person_test1.__proto__)
  9. console.log(person_test2.__proto__)
  10. console.log(Human.prototype === person_test1.__proto__) // true
  11. console.log(Human.prototype === person_test2.__proto__) // true

我們會發現Human.prototype是一個對象,Human類的實例化對象person_test1、person_test2下都有一個屬性__proto__也是對象,并且它們都等于Human.prototype,我們知道在Javascript中引用類型的相等意味著他們所指向的是同一個對象。所以我們可以得到結論,任何一個實例化對象的__proto__屬性都指向其類的prototype。
3.對象的__proto__有什么作用?

</>復制代碼

  1. // 代碼3.3
  2. var Pproto = {
  3. name:"jack"
  4. }
  5. var person = {
  6. __proto__:Pproto
  7. }
  8. console.log(person.name) // jack
  9. person.name = "joker"
  10. console.log(person.name) // joker

我們發現最開始我們并沒有給person定義name屬性,為什么console出來jack呢?這就是Javascript著名的原型鏈的結果啦。話不多說,先上圖:

當我們訪問person.name時,發生了什么呢?
首先它會訪問person對象本身的屬性,如果本身沒有定義name屬性的話,它會去尋找它的__proto__屬性對象,在這個例子中person的__proto__屬性對應的是Pproto對象,所以person的__proto__指向了Pproto,然后我們發現Pproto對象是具有name屬性的,那么person.name就到此為止,返回了jack,但是如果我們又給person加上了一個自身的屬性name呢?這時,再次person.name就不會再尋找__proto__了,因為person本身已經具有了name屬性,而且其值為joker,所以這里會返回joker.

</>復制代碼

  1. 我們注意到上圖中Pproto的__proto__指向了Object,這是因為每一個通過字面量的方式創建出來的對象它們都默認是Object類的對象,所以它們的__proto__自然指向Object.prototype。

4.利用prototype實現靜態【屬性/方法】

</>復制代碼

  1. // 代碼3.4
  2. function Human(name) {
  3. this.name = name
  4. }
  5. Human.prototype.eat = function () {
  6. console.log("I eat!")
  7. }
  8. var person_1 = new Human("Jack")
  9. var person_2 = new Human("Rose")
  10. person_1.eat() // I eat!
  11. person_2.eat() // I eat!
  12. console.log(person_1.eat === person_2.eat) // true

這里我們在構造函數外多寫了一句:Human.prototype.eat = function() {...} 這樣以后每個通過Human實例化的對象的__proto__都會指向Human.prototype,并且根據上述原型鏈知識,我們可以知道只要構造函數中沒有定義同名的非靜態【屬性/方法】,那么每個對象訪問say方法時,訪問的其實都是Human.prototype.say方法,這樣我們就利用prototype實現了類的靜態【屬性/方法】,所有的對象實現了共有的特性,那就是eat

四、繼承的實現

1.我對繼承的理解
假如有n(n>=2)個類,他們的一些【屬性/方法】不一樣,但是也有一些【屬性/方法】是相同的,所以我們每次定義它們的時候都要重復的去定義這些相同的【屬性/方法】,那樣豈不是很煩?所以一些牛逼的程序員想到,能不能像兒子繼承父親的基因一樣,讓這些類也像“兒子們”一樣去“繼承”他們的“父親”(而這里的父親就是包含他們所具有的相同的【屬性/方法】)。這樣我們就可以多定義一個類,把它叫做父類,在它的里面包含所有的這些子類所具有的相同的【屬性/方法】,然后通過繼承的方式,讓所有的子類都可以訪問這些【屬性/方法】,而不用每次都在子類的定義中去定義這些【屬性/方法】了。
2.原型鏈實現繼承(讓子類繼承了父類的靜態【屬性/方法】)

</>復制代碼

  1. // 代碼4.1
  2. function Father() {
  3. }
  4. Father.prototype.say = function() {
  5. console.log("I am talking...")
  6. }
  7. function Son() {
  8. }
  9. var sonObj_1 = new Son()
  10. console.log(sonObj_1.say) // undefined
  11. // 原型鏈實現繼承的關鍵代碼
  12. Son.prototype = new Father()
  13. var sonObj_2 = new Son()
  14. console.log(sonObj_2.say) // function() {...}

看到這句Son.prototype = new Father()你可能有點蒙圈,沒關系,我先上個原型鏈的圖,你分分鐘就能明白了

對著圖我們想一想,首先,一開始Son、Father兩個類沒有什么關系,所以在訪問say的時候肯定是undefined,但是當我們使用了Son.prototype = new Father()后,我們知道通過new Son()生成的對象都會有__proto__屬性,而這個屬性指向Son.prototype,而這里我們又讓它等于了一個Father的對象,而Father類又定義了靜態方法say,所以這里我們的sonObj_2通過沿著原型鏈尋找,尋找到了say方法,于是就可以訪問到Father類的靜態方法say了。這樣就實現了子類繼承了父類的靜態【屬性/方法】,那么如何讓子類繼承父類的非靜態【屬性/方法】呢?
3.構造函數實現繼承(讓子類繼承了父類的非靜態【屬性/方法】)

</>復制代碼

  1. // 代碼4.3
  2. function Father(name) {
  3. this.name = name
  4. }
  5. function Son() {
  6. Father.apply(this, arguments)
  7. this.sing = function() {
  8. console.log(this.name + " is singing...")
  9. }
  10. }
  11. var sonObj_1 = new Son("jack")
  12. var sonObj_2 = new Son("rose")
  13. sonObj_1.sing() // jack is singing...
  14. sonObj_2.sing() // rose is singing...

在這個例子中,通過在Son的構造函數中利用apply函數,執行了Father的構造函數,所以每一個Son對象實例化的過程中都會執行Father的構造函數,從而得到name屬性,這樣,每一個Son實例化的Son對象都會有不同的name屬性值,于是就實現了子類繼承了父類的非靜態【屬性/方法】

4.組合方式實現繼承(組合 原型鏈繼承 + 構造函數繼承)
顧名思義,就是結合上述兩種方法,然后同時實現對父類的靜態及非靜態【屬性/方法】的繼承,代碼如下:

</>復制代碼

  1. // 代碼4.4
  2. function Father(name) {
  3. this.name = name
  4. }
  5. Father.prototype.sayName = function() {
  6. console.log("My name is " + this.name)
  7. }
  8. function Son() {
  9. Father.apply(this, arguments)
  10. }
  11. Son.prototype = new Father("father")
  12. var sonObj_1 = new Son("jack")
  13. var sonObj_2 = new Son("rose")
  14. sonObj_1.sayName() // My name is jack
  15. sonObj_2.sayName() // My name is rose

這里子類Son沒有一個自己的方法,它的sayName方法繼承自父類的靜態方法sayName,構造函數中繼承了父類的構造函數方法,所以得到了非靜態的name屬性,因此它的實例對象都可以調用靜態方法sayName,但是因為它們各自的name不同,所以打印出來的name的值也不同。看到這里,大家可能認為這已經是一種完美無缺的Javascript的繼承方式了,但是還差一丟丟,因為原型鏈繼承不是一種純粹的繼承原型的方式,它有副作用,為什么呢?因為在我們調用Son.prototype = new Father()的時候,不僅僅使Son的原型指向了一個Father的實例對象,而且還讓Father的構造函數執行了一遍,這樣就會執行this.name = name;所以這個Father對象就不純粹了,它具有了name屬性,并且值為father,那為什么之后我們訪問的時候訪問不到這個值呢?這又是因為原型鏈的原因啦,話不多說先上圖:


所以這里父類的構造函數在進行原型鏈繼承的時候也執行了一次,并且在原型鏈上生成了一個我們永遠也不需要訪問的name屬性,而這肯定是占內存的(想象一下name不是一個字符串,而是一個對象),那么我們怎么能讓原型鏈繼承更純粹一點呢?讓它只繼承原型(靜態【屬性/方法】)呢?

5.寄生組合方式實現繼承
為了讓原型鏈繼承的更純粹,這里我們引入一個Super函數,讓Father的原型寄生在Super的原型上,然后讓Son去繼承Super,最后我們把這個過程放到一個閉包內,這樣Super就不會污染全局變量啦,話不多說上代碼:

</>復制代碼

  1. // 代碼4.4
  2. function Father(name) {
  3. this.name = name
  4. }
  5. Father.prototype.sayName = function() {
  6. console.log("My name is " + this.name)
  7. }
  8. function Son() {
  9. Father.apply(this, arguments)
  10. }
  11. (function () {
  12. function Super(){}
  13. Super.prototype = Father.prototype
  14. Son.prototype = new Super()
  15. }())
  16. var sonObj_1 = new Son("jack")

這個時候再去打印sonObj1就會發現,它的原型中已經沒有name屬性啦,如下所示:

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95328.html

相關文章

  • 面向 JavaScript

    摘要:是完全的面向對象語言,它們通過類的形式組織函數和變量,使之不能脫離對象存在。而在基于原型的面向對象方式中,對象則是依靠構造器利用原型構造出來的。 JavaScript 函數式腳本語言特性以及其看似隨意的編寫風格,導致長期以來人們對這一門語言的誤解,即認為 JavaScript 不是一門面向對象的語言,或者只是部分具備一些面向對象的特征。本文將回歸面向對象本意,從對語言感悟的角度闡述為什...

    novo 評論0 收藏0
  • 理解js原型繼承

    摘要:相當于在用原型繼承編寫復雜代碼前理解原型繼承模型十分重要。同時,還要清楚代碼中原型鏈的長度,并在必要時結束原型鏈,以避免可能存在的性能問題。 js是一門動態語言,js沒有類的概念,ES6 新增了class 關鍵字,但只是語法糖,JavaScript 仍舊是基于原型。 至于繼承,js的繼承與java這種傳統的繼承不一樣.js是基于原型鏈的繼承. 在javascript里面,每個對象都有一...

    wthee 評論0 收藏0
  • 前端進擊巨人(七):走進面向象,原型原型繼承方式

    摘要:除了以上介紹的幾種對象創建方式,此外還有寄生構造函數模式穩妥構造函數模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向對象 是以 對象 為中心的編程思想,它的思維方式是構造。 面向對象 編程的三大特點:封裝、繼承、多態: 封裝:屬性方法的抽象 繼承:一個類繼承(復制)另一個類的屬性/方法 多態:方...

    wums 評論0 收藏0
  • 理解JavaScript核心知識點:原型

    摘要:首先,需要來理清一些基礎的計算機編程概念編程哲學與設計模式計算機編程理念源自于對現實抽象的哲學思考,面向對象編程是其一種思維方式,與它并駕齊驅的是另外兩種思路過程式和函數式編程。 JavaScript 中的原型機制一直以來都被眾多開發者(包括本人)低估甚至忽視了,這是因為絕大多數人沒有想要深刻理解這個機制的內涵,以及越來越多的開發者缺乏計算機編程相關的基礎知識。對于這樣的開發者來說 J...

    iKcamp 評論0 收藏0
  • 理解原型原型繼承

    摘要:原型鏈和構造函數是一種面向對象的語言,并且可以進行原型繼承。來了極大的支持了工程化,它的標準讓瀏覽器內部實現類和類的繼承構造函數構造函數調用父類構造函數現在瀏覽器對其支持程度還不高。 原型鏈 原型鏈比作用域鏈要好理解的多。 JavaScript中的每個對象,都有一個內置的_proto_屬性。這個屬性是編程不可見的(雖然ES6標準中開放了這個屬性,然而瀏覽器對這個屬性的可見性的支持不同)...

    XboxYan 評論0 收藏0

發表評論

0條評論

sevi_stuo

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<