理解原型與原型鏈

原型與原型鏈是學習JavaScript這門語言不能不理解的兩個重要概念,爲何?由於JavaScript是一門基於原型的語言。javascript

怎麼理解「JavaScript是一門基於原型的語言」?在軟件設計模式中,有一種模式叫作原型模式,JavaScript正是利用這種模式而被建立出來。先來了解下原型模式的概念:原型模式是用於建立重複的對象,同時又能保證性能。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。這種模式是實現了一個原型接口,該接口用於建立當前對象的克隆。原型模式的目的是用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。也就是說利用已有的一個原型對象,能夠快速地生成和原型對象同樣的新對象實例。
java

原型:設計模式

原型究竟是什麼?原型是一個能夠被複制(或者叫克隆)的一個類,經過複製原型能夠建立一個如出一轍的新對象。通俗的說,原型就是一個模板,在設計語言中更準確的說是一個對象模板數組

var Person = function(name){
    this.name = name;
}
            
Person.prototype.sayHi = function(){
    console.log("Hello! I am " + this.name + ".");
}
            
var tom = new Person("tom");
var mary = new Person("mary");
            
tom.sayHi();    //Hello! I am tom.
mary.sayHi();   //Hello! I am mary.複製代碼

從上面的代碼能夠看出來,原型(Person)定義了一些公用的屬性和方法;利用原型(Person)建立出來的新對象實例(tom和mary對象)會共享原型(Person)的全部屬性和方法。bash

var Person = function(name){
    this.name = name;
}
            
Person.prototype.sayHi = function(){
    console.log("Hello! I am " + this.name + ".");
}
            
var tom = new Person("tom");
var mary = new Person("mary");
            
tom.sayHi();    //Hello! I am tom.
mary.sayHi();   //Hello! I am mary.
tom.getName = function(){
    console.log(this.name);
}
tom.getName();//tom
mary.getName();//undefined複製代碼

此次新對象實例tom添加getName方法,最後只有實例tom擁有該方法,而另外一個實例mary並無getName方法,這說明:經過原型建立的新實例是相互獨立的。函數

也正是由於這樣對象的使用才能更加靈活、更加易於擴展。性能

原型鏈學習

在JavaScript中,全部的對象都擁有一個__proto__屬性指向該對象的原型(prototype)。在上面的代碼中,執行console.log(tom.__proto__)後在控制檯能夠看到輸出了原型(Person),而後繼續執行console.log(Person.__proto__),控制檯打印的結果是function(){},爲何Person的原型是function呢?這是由於Person是一個構造函數,它的本質就是一個函數。在 JavaScript中函數是一等對象。ui

如今用箭頭符號來表示上面原型建立的過程:this

tom.__proto__ => Person
Person.__proto__ => function(){}複製代碼

因而可知,原型鏈記錄了原型對象建立的過程,所以,我給原型鏈添加一個概念:原型鏈是原型對象建立過程的歷史記錄。

var A = {
  a:1,
  b:2
};
                
var B = Object.create(A);
var C = Object.create(B);
            
B.c = 3;
C.d = 4;

console.log(C)    //{d:4}
            
for(var key in C){
  console.log(C[key])
}

//會在控制檯中依次打印出4 3 1 2複製代碼

注意在控制檯打印的順序是4 3 1 2,而不是1 2 3 4。爲何?由於C的原型是B,B的原型是A。C對象只有一個d屬性,執行for...in是先返回C.d,而後再從C的原型(B)中查找到並返回B.c,再從B的原型(A)中查找到並返回A.a和A.b,最後從A的原型(Object)中查找,發現Object中沒有任何屬性,因而結束for...in 。因此最後的結果4 3 1 2。這就解釋了原型繼承是查找屬性的過程是先查找自身屬性,當自身屬性不存在是,會在原型鏈中逐漸查找。

有時只須要查找對象自身的屬性,那麼,這種消耗就是一種浪費,怎麼解決呢?

hasOwnProperty函數

hasOwnProperty函數能夠用來檢查對象自身是否含有某個屬性,返回值是布爾值,當屬性不存在時不會向上查找對象原型鏈。

var A = {
  a:1,
  b:2
};
                
var B = Object.create(A);
var C = Object.create(B);
            
B.c = 3;
C.d = 4;

console.log(C)    //{d:4}
            

for(var key in C){
  console.log(C["a"]) //屬性 a 是原型鏈上的屬性, 輸出 1
}

if(C.hasOwnProperty("a")){
  console.log(C["a"]) //屬性 a 不是自身屬性,不會執行這一步
}複製代碼

hasOwnProperty函數只能檢查對象是否擁有某個數學系,那如何遍歷對象的自身屬性?

getOwnPropertyNames 函數:

getOwnPropertyNames 函數能夠獲取對象全部的自身屬性,返回值是由對象自身屬性名稱組成的數組,一樣不會向上查找對象原型鏈。

var A = {
  a:1,
  b:2
};
                
var B = Object.create(A);
var C = Object.create(B);
            
B.c = 3;
C.d = 4;

console.log(C)    //{d:4}
            
console.log(Object.getOwnPropertyNames(C))  //輸出 ["d"]複製代碼

利用getOwnPropertyNames函數遍歷全部對象全部自身屬性,例:

var f = function(){
  var o = {a:1, b:2};
  var propertys = Object.getOwnPropertyNames(o);
  var len = propertys.length;

  for(var i = 0; i < len; i++){
    var key = propertys[i];
    console.log(o[key]);//輸出 1, 2
  }
};
f();複製代碼
相關文章
相關標籤/搜索