最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
當前位置: 首頁 - 科技 - 知識百科 - 正文

深入理解JavaScript原型概念代碼示例(圖)

來源:懂視網 責編:小采 時間:2020-11-27 20:24:16
文檔

深入理解JavaScript原型概念代碼示例(圖)

深入理解JavaScript原型概念代碼示例(圖):原型是JavaScript中一個比較難理解的概念,原型相關的屬性也比較多,對象有[[prototype]]屬性,函數對象有prototype屬性,原型對象有constructor屬性。為了弄清原型,以及原型相關的這些屬性關系,就有了這篇文章。相信通過這篇文章一定能夠清楚
推薦度:
導讀深入理解JavaScript原型概念代碼示例(圖):原型是JavaScript中一個比較難理解的概念,原型相關的屬性也比較多,對象有[[prototype]]屬性,函數對象有prototype屬性,原型對象有constructor屬性。為了弄清原型,以及原型相關的這些屬性關系,就有了這篇文章。相信通過這篇文章一定能夠清楚
原型是JavaScript中一個比較難理解的概念,原型相關的屬性也比較多,對象有”[[prototype]]”屬性,函數對象有”prototype”屬性,原型對象有”constructor”屬性。

為了弄清原型,以及原型相關的這些屬性關系,就有了這篇文章。

相信通過這篇文章一定能夠清楚的認識到原型,現在就開始原型之旅吧。

認識原型

開始原型的介紹之前,首先來認識一下什么是原型?

在JavaScript中,原型也是一個對象,通過原型可以實現對象的屬性繼承,JavaScript的對象中都包含了一個” [[Prototype]]”內部屬性,這個屬性所對應的就是該對象的原型。

“[[Prototype]]”作為對象的內部屬性,是不能被直接訪問的。所以為了方便查看一個對象的原型,Firefox和Chrome中提供了”__proto__”這個非標準(不是所有瀏覽器都支持)的訪問器(ECMA引入了標準對象原型訪問器”Object.getPrototype(object)”)。

實例分析

下面通過一個例子來看看原型相關概念:

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

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

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

在上面的代碼中,通過了Person這個構造函數創建了一個will對象。下面就通過will這個對象一步步展開了解原型。

Step 1: 查看對象will的原型

通過下面代碼,可以查看對象will的原型:

console.log(will.__proto__);
console.log(will.constructor);

結果分析:

  • “Person {}”對象就是對象will的原型,通過Chrome展開可以看到,”Person {}”作為一個原型對象,也有”__proto__”屬性(對應原型的原型)。

  • 在這段代碼中,還用到了”constructor”屬性。在JavaScript的原型對象中,還包含一個”constructor”屬性,這個屬性對應創建所有指向該原型的實例的構造函數。

  • 通過”constructor”這個屬性,我們可以來判斷一個對象是不是數組類型

    function isArray(myArray) {
     return myArray.constructor.toString().indexOf("Array") > -1;
    }
  • 在這里,will對象本身并沒有”constructor”這個屬性,但是通過原型鏈查找,找到了will原型(will.__proto__)的”constructor”屬性,并得到了Person函數。

  • Step 2: 查看對象will的原型(will.__proto__)的原型

    既然will的原型”Person {}”也是一個對象,那么我們就同樣可以來查看”will的原型(will.__proto__)的原型”。

    運行下面的代碼:

    console.log(will.__proto__ === Person.prototype);
    console.log(Person.prototype.__proto__);
    console.log(Person.prototype.constructor);
    console.log(Person.prototype.constructor === Person);

    結果分析:

  • 首先看 “will.__proto__ === Person.prototype”,在JavaScript中,每個函數都有一個prototype屬性,當一個函數被用作構造函數來創建實例時,該函數的prototype屬性值將被作為原型賦值給所有對象實例(也就是設置實例的__proto__屬性),也就是說,所有實例的原型引用的是函數的prototype屬性。了解了構造函數的prototype屬性之后,一定就明白為什么第一句結果為true了。

  • prototype屬性是函數對象特有的,如果不是函數對象,將不會有這樣一個屬性。

  • 當通過”Person.prototype.__proto__”語句獲取will對象原型的原型時候,將得到”Object {}”對象,后面將會看到所有對象的原型都將追溯到”Object {}”對象。

  • 對于原型對象”Person.prototype”的”constructor”,根據前面的介紹,將對應Person函數本身。

  • 通過上面可以看到,“Person.prototype”對象和Person函數對象通過”constructor”和”prototype”屬性實現了相互引用(后面會有圖展示這個相互引用的關系)。

    Step 3: 查看對象Object的原型

    通過前一部分可以看到,will的原型的原型是”Object {}”對象。實際上在JavaScript中,所有對象的原型都將追溯到”Object {}”對象。

    下面通過一段代碼看看”Object {}”對象:

    console.log(Person.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的原型

    在上面的例子中,Person是一個構造函數,在JavaScript中函數也是對象,所以,我們也可以通過”__proto__”屬性來查找Person函數對象的原型。

    console.log(Person.__proto__ === Function.prototype);
    console.log(Person.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__屬性)

  • 圖解實例

    通過上面結合實例的分析,相信你一定了解了原型中的很多內容。

    但是現在肯定對上面例子中的關系感覺很凌亂,一會兒原型,一會兒原型的原型,還有Function,Object,constructor,prototype等等關系。

    現在就對上面的例子中分析得到的結果/關系進行圖解,相信這張圖可以讓你豁然開朗。

    對于上圖的總結如下:

  • 所有的對象都有”__proto__”屬性,該屬性對應該對象的原型

  • 所有的函數對象都有”prototype”屬性,該屬性的值會被賦值給該函數創建的對象的”__proto__”屬性

  • 所有的原型對象都有”constructor”屬性,該屬性對應創建所有指向該原型的實例的構造函數

  • 函數對象和原型對象通過”prototype”和”constructor”屬性進行相互關聯

  • 通過原型改進例子

    在上面例子中,”getInfo”方法是構造函數Person的一個成員,當通過Person構造兩個實例的時候,每個實例都會包含一個”getInfo”方法。

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

    前面了解到,原型就是為了方便實現屬性的繼承,所以可以將”getInfo”方法當作Person原型(Person.__proto__)的一個屬性,這樣所有的實例都可以通過原型繼承的方式來使用”getInfo”這個方法了。

    所以對例子進行如下修改:

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

    修改后的結果為:

    原型鏈

    因為每個對象和原型都有原型,對象的原型指向對象的父,而父的原型又指向父的父,這種原型層層連接起來的就構成了原型鏈。

    在”理解JavaScript的作用域鏈“一文中,已經介紹了標識符和屬性通過作用域鏈和原型鏈的查找。

    這里就繼續看一下基于原型鏈的屬性查找。

    屬性查找

    當查找一個對象的屬性時,JavaScript 會向上遍歷原型鏈,直到找到給定名稱的屬性為止,到查找到達原型鏈的頂部(也就是 “Object.prototype”), 如果仍然沒有找到指定的屬性,就會返回 undefined。

    看一個例子:

    function Person(name, age){
     this.name = name;
     this.age = age;
    }
    
    Person.prototype.MaxNumber = 9999;
    Person.__proto__.MinNumber = -9999;
    
    var will = new Person("Will", 28);
    
    console.log(will.MaxNumber);
    // 9999
    console.log(will.MinNumber);
    // undefined

    在這個例子中分別給”Person.prototype “和” Person.__proto__”這兩個原型對象添加了”MaxNumber “和”MinNumber”屬性,這里就需要弄清”prototype”和”__proto__”的區別了。

    “Person.prototype “對應的就是Person構造出來所有實例的原型,也就是說”Person.prototype “屬于這些實例原型鏈的一部分,所以當這些實例進行屬性查找時候,就會引用到”Person.prototype “中的屬性。

    屬性隱藏

    當通過原型鏈查找一個屬性的時候,首先查找的是對象本身的屬性,如果找不到才會繼續按照原型鏈進行查找。

    這樣一來,如果想要覆蓋原型鏈上的一些屬性,我們就可以直接在對象中引入這些屬性,達到屬性隱藏的效果。

    看一個簡單的例子:

    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);
    will.getInfo = function(){
     console.log("getInfo method from will instead of prototype");
    };
    
    will.getInfo();
    // getInfo method from will instead of prototype

    對象創建方式影響原型鏈

    會到本文開始的例子,will對象通過Person構造函數創建,所以will的原型(will.__proto__)就是”Person.prototype”。

    同樣,我們可以通過下面的方式創建一個對象:

    var July = {
     name: "July",
     age: 28,
     getInfo: function(){
     console.log(this.name + " is " + this.age + " years old");
     },
    }
    
    console.log(July.getInfo());

    當使用這種方式創建一個對象的時候,原型鏈就變成下圖了,July對象的原型是”Object.prototype”也就是說對象的構建方式會影響原型鏈的形式。

    hasOwnProperty

    “hasOwnProperty”是”Object.prototype”的一個方法,該方法能判斷一個對象是否包含自定義屬性而不是原型鏈上的屬性,因為”hasOwnProperty” 是 JavaScript 中唯一一個處理屬性但是不查找原型鏈的函數。

    相信你還記得文章最開始的例子中,通過will我們可以訪問”constructor”這個屬性,并得到will的構造函數Person。這里結合”hasOwnProperty”這個函數就可以看到,will對象并沒有”constructor”這個屬性。

    從下面的輸出可以看到,”constructor”是will的原型(will.__proto__)的屬性,但是通過原型鏈的查找,will對象可以發現并使用”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”屬性進行相互關聯

  • 還有要強調的是文章開始的例子,以及通過例子得到的一張”普通對象”,”函數對象”和”原型對象”之間的關系圖,當你對原型的關系迷惑的時候,就想想這張圖(或者重畫一張當前對象的關系圖),就可以理清這里面的復雜關系了。

    通過這些介紹,相信一定可以對原型有個清晰的認識。

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    深入理解JavaScript原型概念代碼示例(圖)

    深入理解JavaScript原型概念代碼示例(圖):原型是JavaScript中一個比較難理解的概念,原型相關的屬性也比較多,對象有[[prototype]]屬性,函數對象有prototype屬性,原型對象有constructor屬性。為了弄清原型,以及原型相關的這些屬性關系,就有了這篇文章。相信通過這篇文章一定能夠清楚
    推薦度:
    標簽: 理解 實例 概念
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    国产精品久久久久精品…-国产精品可乐视频最新-亚洲欧美重口味在线-欧美va免费在线观看