一篇文章讓你完全瞭解 JS 原型鏈

前言

原型是 JavaScript 中一個比較難理解的概念,原型相關的屬性也比較多,對象有"[[prototype]]"屬性,函數對象有"prototype"屬性,原型對象有"constructor"屬性。segmentfault

認識原型(什麼是原型)

開始原型的介紹以前,咱們首先說說什麼是原型?數組

定義

在 JavaScript 中,原型也是一個對象,經過原型能夠實現對象的屬性繼承,JavaScript 的對象中都包含了一個"[[prototype]]"內部屬性,這個屬性所對應的就是該對象的原型。
"[[prototype]]"做爲對象的內部屬性,是不能被直接訪問的。全部爲了方便查看一個對象的原型,Chrome 等大型瀏覽器廠商提供了"__proto__"這個非標準的訪問器(ECMA 引入了標準對象原型訪問器 "Object.getPrototype(Object)")瀏覽器

實例分析
function Animal(name, type) {
        this.name = name;
        this.type = type;
        this.getInfo = function(){
            console.info("當前動物屬性==>",this.name + 'is' + this.type)
        }
    }
    let dog = new Animal("狗", "犬科哺乳動物") // 當前動物屬性==> 狗is犬科哺乳動物

Step-->1: 查看對象 dog 的原型函數

console.info("__proto__",dog.__proto__);
    // __proto__ Objectconstructor: ƒ Animal(name, type)__proto__:
    console.info("constructor=====>",dog.constructor)
     //constructor=====> ƒ Animal(name, type) {
     //       this.name = name;
     //       this.type = type;
     //       this.getInfo = function(){
    //      console.info("當前動物屬性==>",this.name + 'is' +     this.type)
結果分析
  • "Animal{}"對象就是 dog 的原型,經過 Chrome 展開能夠看到,"Animal{}"做爲一個原型對象,也有"__proto__"屬性(對應原型的原型)
  • 在這段代碼中,還用到"constructor"屬性。在 JavaScript的對象原型對象中。還包含一個"constructor"屬性,這個屬性對應建立全部指向該原型的實例的構造函數
// 拓展 能夠判斷一個對象是否是數組類型
    function isArray(arr){
        return arr.constructor.toString().indexOf("Array") > -1;
    }

*在這裏,dog 對象自己沒有"constructor"這個屬性,可是經過原型鏈查找,這到了 dog 原型(dog.__proto__)的 "constructor"屬性,並找到了Animal函數this

Step-->2: 查看對象 dog 的原型(dog.__proto__)的原型spa

既然 dog 的原型"Animal{}"也是一個對象,那麼咱們就一樣能夠來查看 dog 的原型(dog.__proto__)的原型prototype

console.info(dog.__proto__ === Animal.prototype)
    console.info(Animal.prototype.__proto__)
    console.info(Animal.prototype.constructor)
    console.info(Animal.prototype.constructor === Animal)
結果分析
  • 首先看"dog.__proto__ === Animal.prototype",在 JavaScript 中,每一個函數都有一個 prototype 屬性,當一個函數被用做構造函數來建立實例時,該函數的 prototype 屬性值將被做爲原型賦值給全部對象實例(也就是設置實例的__proto__屬性),也就是說,全部實例的原型引用的是函數的 prototype 屬性。瞭解了構造函數的 prototype 屬性以後。必定就會明白爲何第一句是 true 了
  • 當經過"Animal.prototype.__proto__"語句獲取dog 對象原型的原型時候,將獲得"Object{}"對象,後面將會看到全部對象的原型都將追溯到"Object{}"對象。
  • 對於原型對象"Animal.prototype"對象的"constructor"根據前面的介紹,將對應 Animal 函數自己。

經過上面能夠看到,"Animal.prototype"對象和 Animal 函數對象經過"constructor"和 "prototype"屬性實現了相互引用code

Step-->3: 查看對象 Object 的原型對象

經過前一部分能夠看到,will的原型的原型是"Object{}"對象。實際上在JavaScript中,全部對象的原型都將追溯到"Object {}"對象。
下面經過一段代碼看看"Object {}"對象:blog

console.log(Animal.prototype.__proto__ === Object.prototype);
    console.log(typeof Object);
    console.log(Object);
    console.log(Object.prototype);
    console.log(Object.prototype.__proto__);
    console.log(Object.prototype.constructor);
結果分析
  • Object對象自己是一個函數對象。
  • 既然是Object函數,就確定會有prototype屬性,因此能夠看到"Object.prototype"的值就是"Object {}"這個原型對象。
  • 反過來,當訪問"Object.prototype"對象的"constructor"這個屬性的時候,就獲得了Obejct函數。
  • 另外,當經過"Object.prototype.__proto__"獲取Object原型的原型的時候,將會獲得"null",也就是說"Object {}"原型對象就是原型鏈的終點了。

Step-->4: 查看對象Function的原型

在上面的例子中,Animal是一個構造函數,在JavaScript中函數也是對象,因此,咱們也能夠經過"__proto__"屬性來查找Animal函數對象的原型。

console.log(Animal.__proto__ === Function.prototype);
    console.log(Animal.constructor === Function)
    console.log(typeof Function);
    console.log(Function);
    console.log(Function.prototype);
    console.log(Function.prototype.__proto__);
    console.log(Function.prototype.constructor);
結果分析
  • 在JavaScript中有個Function對象(相似Object),這個對象自己是個函數;全部的函數(包括Function,Object)的原型(__proto__)都是"Function.prototype"。
  • Function對象做爲一個函數,就會有prototype屬性,該屬性將對應"function () {}"對象。
  • Function對象做爲一個對象,就有"__proto__"屬性,該屬性對應"Function.prototype",也就是說,"Function.__proto__ === Function.prototype"
  • 對於Function的原型對象"Function.prototype",該原型對象的"__proto__"屬性將對應"Object {}"
對比 prototype 和 proto

對於"prototype"和"__proto__"這兩個屬性有的時候可能會弄混,"Person.prototype"和"Person.__proto__"是徹底不一樣的。

在這裏對"prototype"和"__proto__"進行簡單的介紹:

  • 對於全部的對象,都有__proto__屬性,這個屬性對應該對象的原型
  • 對於函數對象,除了__proto__屬性以外,還有prototype屬性,當一個函數被用做構造函數來建立實例時,該函數的prototype屬性值將被做爲原型賦值給全部對象實例(也就是設置實例的__proto__屬性)

原型鏈

對於上圖的總結以下:
  • 全部的對象都有"__proto__"屬性,該屬性對應該對象的原型
  • 全部的函數對象都有"prototype"屬性,該屬性的值會被賦值給該函數建立的對象的"__proto__"屬性
  • 全部的原型對象都有"constructor"屬性,該屬性對應建立全部指向該原型的實例的構造函數
  • 函數對象和原型對象經過"prototype"和"constructor"屬性進行相互關聯
  • 由於每一個對象和原型都有原型,對象的原型指向對象的父,而父的原型又指向父的父,這種原型層層鏈接起來的就構成了原型鏈。
hasOwnProperty

"hasOwnProperty"是"Object.prototype"的一個方法,該方法能判斷一個對象是否包含自定義屬性而不是原型鏈上的屬性,由於"hasOwnProperty" 是 JavaScript 中惟一一個處理屬性可是不查找原型鏈的函數。

相信你還記得文章最開始的例子中,經過dog咱們能夠訪問"constructor"這個屬性,並獲得dog的構造函數Animal。這裏結合"hasOwnProperty"這個函數就能夠看到,dog對象並無"constructor"這個屬性。

從下面的輸出能夠看到,"constructor"是dog的原型(dog.__proto__)的屬性,可是經過原型鏈的查找,dog對象能夠發現並使用"constructor"屬性。

"hasOwnProperty"還有一個重要的使用場景,就是用來遍歷對象的屬性。

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
};


var will = new Person("Will", 28);

for(var attr in will){
    console.log(attr);
}
// name
// age
// getInfo

for(var attr in will){
    if(will.hasOwnProperty(attr)){
        console.log(attr);
    }
}
// name
// age

總結

本文介紹了JavaScript中原型相關的概念,對於原型能夠概括出下面一些點:

全部的對象都有"[[prototype]]"屬性(經過__proto__訪問),該屬性對應對象的原型
全部的函數對象都有"prototype"屬性,該屬性的值會被賦值給該函數建立的對象的"__proto__"屬性
全部的原型對象都有"constructor"屬性,該屬性對應建立全部指向該原型的實例的構造函數
函數對象和原型對象經過"prototype"和"constructor"屬性進行相互關聯
經過這些介紹,相信必定能夠對原型有個清晰的認識。
圖片

相關文章
相關標籤/搜索