詳談JavaScript原型鏈


 本文是學習中傳思客在慕課網開的課程《前端跳槽面試必備技巧》的學習筆記。課程地址:https://coding.imooc.com/class/evaluation/129.html#Anchor

目錄

  1. 創建對象有幾種方法
  2. 原型、構造函數、實例、原型鏈
  3. instanceof的原理
  4. new運算符

 創建對象的方法

  在了解原型鏈之前,首先先了解一下創建對象的幾種方式,介紹以下三種。

代碼:

<script type="text/javascript">
    // 第一種方式:字面量
    var o1 = {name: 'o1'}
    var o2 = new Object({name: 'o2'})
      // 第二種方式:構造函數
    var M = function (name) { this.name = name; }
    var o3 = new M('o3')
      // 第三種方式:Object.create
    var p = {name: 'p'}
    var o4 = Object.create(p)

  console.log(o1)    
  console.log(o2)
  console.log(o3)
  console.log(o4)
</script>

 

打印結果:

對象是創建出來了,但你可能對結果很詫異,為什么不同呢?別急,慢慢來。

 原型及原型鏈

先來一張容易讓人懵逼的圖

什么是原型對象?實例?構造函數?

概念就不多說了,看代碼吧

var M = function (name) { this.name = name; }
var o3 = new M('o3')
  •  實例就是對象,在本例中o3就是實例,M就是構造函數。
  • 實例通過new一個構造函數生成的。
  • 從上圖中可以知道,實例的__protpo__指向的是原型對象。
  • 實例的構造函數的prototype也是指向的原型對象。 
  • 原型對象的construor指向的是構造函數。

 

再來通過下面這個圖來理解一下 

 

那什么是原型鏈呢?

  簡單理解就是原型組成的鏈,對象的__proto__它的是原型,而原型也是一個對象,也有__proto__屬性,原型的__proto__又是原型的原型,就這樣可以一直通過__proto__想上找,這就是原型鏈,當向上找找到Object的原型的時候,這條原型鏈就算到頭了。

原型對象和實例之間有什么作用呢?

通過一個構造函數創建出來的多個實例,如果都要添加一個方法,給每個實例去添加並不是一個明智的選擇。這時就該用上原型了。

在實例的原型上添加一個方法,這個原型的所有實例便都有了這個方法。

接着上面的例子繼續演示:

var M = function (name) { this.name = name; }
var o3 = new M('o3')
var o5 = new M()
o3.__proto__.say=furnction(){
   console.log('hello world')
}

o3.say()
o5.say()

 

打印結果

按照JS引擎的分析方式,在訪問一個實例的屬性的時候,現在實例本身中找,如果沒找到就去它的原型中找,還沒找到就再往上找,直到找到。這就是原型鏈。

補充:

只有函數有prototype,對象是沒有的。

但是函數也是有__proto__的,因為函數也是對象。函數的__proto__指向的是Function.prototype。

也就是說普通函數是Function這個構造函數的一個實例。

 

instanceof原理

instanceof是判斷實例對象的__proto__和生成該實例的構造函數的prototype是不是引用的同一個地址。

是返回true,否返回false。

注意:實例的instanceof在比較的時候,與原型鏈上想上找的的構造函數相比都是true。

 繼續上面的代碼

那怎么判斷實例是由哪個構造函數生成的呢?這時候就要用到constructor了。

實例的原型的構造函數, obj.__proto__.constructor

 

new運算符

new運算符的原理

  • 一個新對象被創建。它繼承自foo.prototype。
  • 構造函數返回一個對象。在執行的時候,相應的傳參會被傳入,同時上下文(this)會被指定為這個新的實例。
  • new foo等同於new foo(), 只能用在不傳遞任何參數的情況
  • 如果構造函數反悔了一個對象,那個這個對象會取代整個new出來的結果。如果構造函數沒有返回對象,那個new出來的結果為步驟1創建的對象。

 下面根據new的工作原理通過代碼手動實現一下new運算符

var new2 = function (func) {
    var o = Object.create(func.prototype);    //創建對象
    var k = func.call(o);             //改變this指向,把結果付給k
    if (typeof k === 'object') {         //判斷k的類型是不是對象
        return k;                  //是,返回k
    } else {
        return o;                  //不是返回返回構造函數的執行結果
    }
}    

 

 驗證

 

經過上圖一系列折騰,不難看出,我們手動編寫的new2和new運算符的作用是一樣的。

通過這個例子,你是不是已經熟知了new的工作原理了呢

 

最后,回到回到第一節創建對象的方法那里。

幾種創建對象方法的不同,了解了原型鏈和new之后,是不是此題的答案就出來了,這里我就不細說了,留給讀者一些思考的時間。。。 

 

覺得本文對你有幫助的話,點個贊再走吧

 


免責聲明!

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



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