首頁  >  文章  >  web前端  >  JavaScript原型與原型鏈的詳細分析

JavaScript原型與原型鏈的詳細分析

黄舟
黄舟原創
2017-03-14 15:03:171025瀏覽

JavaScript沒有類別的概念,但幾乎所有的東西又是基於物件的,同時也能實作繼承,這就是js 跟其他OOP語言最大的不同之處,這也是js最難理解的一塊。下面我來說說我個人的理解。

先從建立物件說起,一般會有下面幾種方法:

#1.建立一個Object實例,然後給它加屬性和方法

var person() = new Object();
person.name = 'mikej';
person.sayName = function(){
  alert(this.name);
}

2.也可以這樣寫:

var parson = {
  name : 'mikej',
  sayName : function(){
    alert(this.name);
  }
}

3.這兩種建立物件的方法很簡單,但都有缺陷,使用同一個模式建立物件的時候,會產生大量重複程式碼。於是就有了工廠模式

function createPerson(name){
  var p = new Object();
  p.name=name;
  p.sayName = function(){
    alert(this.name);
  };
  return p;
}
var p1 = createPerson('mikej');
var p2 = createPerson('tom');

這樣就可以無限創建物件了。

4.還有一種方法,跟工廠模式異曲同工,叫做建構子模式:

function Person(name){
  this.name=name
  this.sayName = function(){
   alert(this.name);
  }
  this.say = function(){
    alert('hi');
  }
}
var p1 = new Person('mikej');
var p2 = new Person('tom');

這裡有幾個值得關注的地方:沒有顯示的創建對象、函數名Person使用的是大寫字母P(這是必須的)、p1和p2中都有一個constructor(建構子)屬性,指向Person。同時p1和p2既是Object的實例,也是Person的實例。

alert(p1.constructor == Person); //true
alert(p1 instanceof Object); //true
alert(p1 instanceof Person); //true

//5.11更新:以一個phper的角度看的話,之前很容易將創建物件的流程想成這樣,Person就是一個“類別”,然後用 new Person('mikej')實例化了這個類,並且傳入參數。但實際上並不是這樣的,創建的流程應該是這樣:首先,建立一個空對象,然後用apply方法,第一個參數是這個空對象,第二個參數是上下文的參數,這樣Person中的this就會指向這個對象,也就是p1。

var p1 = new Person('mikej');
//上面代码就相当于
var p1 = {};
Person.apply(p1, ['mikej']);

建構函數模式看起來很好,但是它有一個弊端就是浪費內存,接上例

alert(p1.say == p2.say) //false

.為了避免這個缺陷,可是使用原型模式來創建對象,js中的每個對像都有一個JavaScript原型與原型鏈的詳細分析屬性用來指向另一個對象,這個對象的所有屬性和方法都會被構造函數的實例繼承,是共享的,這就意味著,我們可以把那些不變的屬性和方法,定義到JavaScript原型與原型鏈的詳細分析物件上。

function Person(name){
  this.name = name;
}
//Person的原型对象
Person.JavaScript原型與原型鏈的詳細分析 = {
  say: function(){
    alert('hi');
  },
  sayName: function(){
    alert(this.name);
  }
};
var p1 = new Person("mikej");
var p2 = new Person("tom");
p1.sayName();
p2.sayName();
//下面就可以看出方法实现了共享
alert(P1.say == P2.say) //true
alert(P1.sayName == P2.sayName) //true

再來擴充一下上面的例子,使用原型來實作繼承。

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

Person.JavaScript原型與原型鏈的詳細分析 = {
  say: function(){
    alert('hi');
  },
  sayName: function(){
    alert(this.name);
  }
};

function Programmer(){
  this.say = function(){
    alert('im Programmer, my name is ' + this.name);
  }
}

Programmer.JavaScript原型與原型鏈的詳細分析 = new Person('mikej');
//手动修正构造函数
Programmer.JavaScript原型與原型鏈的詳細分析.constructor = Programmer;
var p1 = new Programmer();

console.dir(Programmer.JavaScript原型與原型鏈的詳細分析.constructor);//Programmer
console.dir(p1.constructor);//Programmer
console.dir(p1);

Programmer的原型指向了Person的一個實例,那麼所有的Programmer的實例都能繼承Person和Person的原型了。

這裡會有一個問題。

預設原型物件裡有一個constructor屬性,指向它的建構子。而每一個實例也有一個constructor屬性,會預設呼叫JavaScript原型與原型鏈的詳細分析物件的constructor屬性。

假設沒有Programmer.JavaScript原型與原型鏈的詳細分析 = new Person('mikej');

Programmer.JavaScript原型與原型鏈的詳細分析.constructor是指向Programmer的。 p1的構造也指向Programmer

alert(Programmer.JavaScript原型與原型鏈的詳細分析.constructor == Programmer) //true
alert(p1.constructor == Programmer) //true

但有了這句Programmer.JavaScript原型與原型鏈的詳細分析 = new Person('mikej');之後,

Programmer.JavaScript原型與原型鏈的詳細分析.constructor就指向了Object,也就是Person.JavaScript原型與原型鏈的詳細分析所指向的物件的建構。 p1.constructor也指向了Object。但p1明明是建構函數Programmer產生的,這就造成了繼承的混亂,所以我們必須手動修正建構函數,也就是下面這程式碼。

Programmer.JavaScript原型與原型鏈的詳細分析.constructor = Programmer;

好了,現在我們再來看看原型鏈:

console.dir(p1);

這句程式碼的結果是

JavaScript原型與原型鏈的詳細分析

可以看出, p1是Programmer的實例,Programmer的原型是Person,Person的原型是Object,再網路上就是JS的物件了。這就是原型鏈,也就是說,JavaScript的繼承是基於原型鏈來實現的。

#

以上是JavaScript原型與原型鏈的詳細分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn