javascript基礎之我見(1)----深度理解原型鏈


寫在開始之前:

  早就想要好好總結下javascript的基礎知識了,所以從這篇文章起,我會開始總結各種js的語法知識,作為一名以js開發為生計的前端工程師,深深的着迷於js的語言魅力,而js最吸引人最強大的地方,便在於他獨特的語法,能深刻的理解js的語法,是作為一個前端工程師的基本素質,在這里,筆者在總結的同時,也希望前端朋友們給予自己的補充和見解。那么就讓我們從js最最獨特的語法,閉包,原型,詞法作用域開始,接下來,筆者也會討論this,正則,瀏覽器的能力檢測,事件代理等細節問題,以及html5,css3等前沿領域,今天我先試着總結下原型鏈相關的知識。筆者的經驗和知識也許會有很多不足之處,歡迎大家的更正與建議。

 

1.首先要知道的:

  什么是原型鏈呢?

  我們首先來說說繼承,繼承是面向對象語言的重要機制,通俗地講就是子類可以擁有父類的方法和屬性,js的原型鏈實際上也是一種繼承,在ECMAScript標准中,只支持實現繼承,而其實現實現繼承就是主要依靠於原型鏈實現的。

  那么,我們再來說說原型,原型其實就是上述所說的繼承中的父類。

  這樣,原型鏈 顯而易見的 可以理解為,利用原型串起一個繼承鏈,讓一個引用類型繼承另一個引用類型的屬性和方法,再以此類推下去。

  構造函數,原型,與實例的關系:

  這三者的關系用一句話概括為,每個構造函數都有一個原型,當new 一個構造函數的時候就會生成一個原型鏈上攜帶該構造函數的原型的實例。

2.原型鏈的生成過程:  

  上面基本了解了原型鏈的相關知識,那么原型鏈的生成過程是什么呢?

1 function parent(){
  this.parent = "world";
2 } 3 parent.prototype = { 4 a : function(){ 5   }, 6 b : "hello" 7 } 8 var child = new parent()

   而child就是parent的一個實例,在執行構造函數的時候,js引擎創建一個空白對象,把__proto__指向parent的prototype,然后把這個對象作為this調用parent進行初始化,這樣一個簡單的原型鏈就形成了 即 child --> parent.prototye -->Object.prototype。

    prototype,constructor,__proto__ : 

  上面多次提到這幾個屬性,在原型鏈的概念中,如果能理解這幾個屬性的關系,那么離勝利就不遠了。下面我們逐一的談談這幾個屬性:

  1.prototype

  prototype是構造函數的屬性,指的就是構造函數的原型,在生成實例的時候,js會根據構造函數的prototype屬性將該屬性下的對象生成為父類,

     注意,只有構造函數這個屬性才有這種效果哦~如果一個構造函數沒有指定該屬性,那么該屬性下的__proto__會默認的指向原生Object的原型對象,該屬性會變成一個對象,其中constructor屬性指向本身。

  2.constructor

     constructor,如果通俗的理解,可以理解成原型對象的構造函數,當把一個對象(對象字面量)賦給一個構造函數的原型,constructor會被復寫,如果沒有進行prototype的操作的話,constructor是函數聲明時指定的,指定為本身:

  例如:

  function A() {}
  則 A.prototype.constructor === A;

但是這個屬性往往是不准確的:

1 function A() {}
2 function B() {}
3 B.prototype = new A();
4 var b = new B();
5 b.constructor; // A

  上面的代碼,按照constructor的含義,b的constructor應該指向B,但是確指向了A,原因如下

    A沒有進行prototype操作,所以其constructor指向本身;
  B.prototype = new A();之后B.prototype被復寫為A的實例,
  則B.prototype.constructor === A;
  而b是B的實例則b.constructor === A;
 
  所以constructor這個屬性是不准確的,不推薦大家關注與使用。
  

  如果想要實現繼承,一般要進行constructor修復,即:
  B.prototype = new A();
  B.prototype.constructor = B;

  3.__proto__
  可以這么說,js的原型鏈就是通過這個屬性串聯起來的,__proto__屬性指向他的父類,在調用一個對象的屬性或者方法的時候就是通過__proto__這一屬性指向的對象一層一層的向上查找的。上面的一句: 在生成實例的時候,js會根據構造函數的prototype屬性將該屬性下的對象生成為父類,在這里可以改為,在生成實例的時候,js會根據構造函數的prototype屬性將該屬性下的對象引用到實例的__proto__屬性下。
     
 1 function parent(){
 2     this.a = "world";
 3     this.b = "world";
 4 }
 5 parent.prototype = {
 6    a : "aaa",
 7    b : "bbb",
 8    c : "!" 
 9 }
10 function temp(){
11    this.a = "hello";
12 }
13 temp.prototype = new parent();
14 var child = new temp();
15 console.log(child.a +" " + child.b+child.c);//hello world!

  上面的代碼運行結果就為 : “hello world!”生成的原型鏈即是

  child(temp的實例) > __proto__ > temp.prototype(parent的實例) > __proto__  >parent.prototype > __proto__ > Object.prototype >__proto__ > null

3.原型鏈的調用

  如上所示,原型鏈由__proto__屬性串聯而成,經過上面的分析,調用的理解就變得很簡單了,比如上例的原型鏈中,當調用child.c屬性時,那么程序會按如下方式運行:

  在child本層查找c,查詢未果,會通過__proto__屬性向上查找temp.prototype,查找未果,繼續沿着__proto__向上查找parent.prototype,oyeah~終於找到了~所以child.c在這里就會是parent.prototype.c了~

  怎么樣,簡單吧^ ^

 

最后的最后,呈上一個常用函數給大家:

  

 1 function A() {
 2      // do something here
 3 }
 4 A.prototype = {
 5      a : 'aaa',
 6      b : function() {
 7      }
 8 };
 9  function B() {
10 }
11 extend(B, A);
12 function extend(child, parent) {
13      function temp() {
14             this.constructor = child;
15     }
16      temp.prototype = parent.prototype;
17      child.prototype = new temp();
18 }

  extend函數 兩個參數都為構造函數,目的是讓一個構造函數(子類)繼承另一個構造函數(父類)的原型,並且不調用父類的構造函數(有時候構造函數會需要一些參數或者做一些事情破壞了原型鏈),這個方法可以用來生成想要的原型鏈哦。

 

  好了,今天就總結這么多,以后繼續總結其他的js語法知識


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM