首頁 >web前端 >js教程 >JavaScript物件導向-基於組合和動態原型創建對象

JavaScript物件導向-基於組合和動態原型創建對象

黄舟
黄舟原創
2017-01-19 15:18:251270瀏覽

前面兩篇文章我們介紹了JavaScript中原型的記憶體模型和原型的重寫方法即註意事項。在了解原型之後,我們就可以透過原型來建立JavaScript物件。基於原型的創建方式雖然可以有效的完成封裝,但是仍然會存在一些問題。

透過原型的方式來建立物件主要會產生2個問題:

  • 1、無法透過建構子來設定物件的屬性值。

  • 2、當屬性中有引用類型變數時,可能會存在變數值的重複。

我們來看下面的例子:

function Person(){}
Person.prototype = {
  constructor:Person,
  name:"Leon",
  age:22,
  friends:["Ada","Chris"],
  say:function(){
    console.info(this.name+"["+this.friends+"]");
  }
}

在上面的程式碼中,我們創建了一個Person類,並透過原型重寫的方式為它設定了一些屬性和方法,其中有一個friends屬性,是一個引用型別的陣列。接下來我們透過Person類別來建立對象,程式碼如下:

var p1 = new Person();
p1.name = "John";
p1.say(); //控制台输出:Jhon[Ada,Chris]

我們透過Person類別建立了物件p1,可以看到使用原型方式建立物件時沒有辦法為物件p1設定屬性,我們只有在物件建立之後才設定p1的name屬性。接著呼叫p1的say()方法,控制台會輸出Jhon[Ada,Chris],到這裡一切都還是正常的。

如果我們接著為物件p1增加一個新的朋友,問題就會出現了。程式碼如下:

p1.friends.push("Mike"); //为p1增加一个朋友(注意这里是在原型中添加)
p1.say();

我們透過數組的push方法為p1添加一個新的朋友“Mike”,此時,在物件p1自己的空間中是沒有friends屬性的,所以“Mike”會被添加到Person的原型中,如下圖所示:

JavaScript物件導向-基於組合和動態原型創建對象

由於新加入的數組元素是放置在原型中的,所以後面創建的所有物件都會共享這個屬性,這時我們創建物件p2的話,他的朋友中也會有一個“Mike”。這是我們不希望看到的結果。

var p2 = new Person();
//如果p1 push之后,原型中就多了一个人,p2也多了一个朋友
p2.say();

 基於組合原型和建構函數的方式來建立物件

為了解決上面的問題,我們可以使用基於組合原型和建構函數的方式來建立物件。也就是將屬性在建構函式中定義,將方法在原型中定義。這種方式有效的集合了兩者的優點,是我們在JavaScript中最常用的一種創建物件的方式。

function Person(name,age,friends){
  //属性在构造函数中定义
  this.name = name;
  this.age = age;
  this.friends = friends;
}
Person.prototype = {
  //方法在原型中定义
  constructor:Person,
  say:function(){
    console.info(this.name+"["+this.friends+"]");
  }
}

透過這種方式創建的對象,所有的屬性都是保存在物件自己的空間中的。此時,在創建物件的時候,我們就可以為物件設定它自己的屬性。

var p1 = new Person("Leon",22,["Ada","Chris"]);
p1.name = "John";
p1.say();   //控制台输出: John[Ada,Chris]

完成上面的程式碼後,Person類別及p1物件的記憶體模型如下圖:

JavaScript物件導向-基於組合和動態原型創建對象

此時,我們再為物件p1新增一個新的朋友時,會在p1物件自己的記憶體空間中的friends屬性中新增。這樣,每個物件的屬性都是獨立的,不會互相干擾。

p1.friends.push("Mike"); //为p1增加一个朋友(注意这里是在p1自己的空间中添加)
p1.say();   //控制台输出: John[Ada,Chris,Mike]
var p2 = new Person();
p2.say();   //控制台输出: John[Ada,Chris]

因此,現在再建立物件p2時,p2物件的friends只會是“Ada”和“Chris”,而沒有“Mike”。

 動態原型方式創建物件

雖然基於組合原型和建構函數的方式創建物件已經非常完美了,但是它和純正的物件導向語言創建物件的方式還是有一些差別:類別的方法被定義在類別之外。為了讓定義物件更符合物件導向規格的需求,我們可以把定義方法的原型程式碼放置到Person建構函式中。這種方式我們稱為動態原型方式創建物件。

// 动态原型方式
function Person(name,age,friends){
  this.name = name;
  this.age = age;
  this.friends = friends;
   
  Person.prototype.say = function(){
    console.info(this.name+"["+this.friends+"]");
  }
}

注意在使用動態原型方式創建物件的時候,我們在定義方法的時候不能夠使用原型重寫的方式,例如下面的程式碼是錯誤的:

// 错误的动态原型方式
function Person(name,age,friends){
  this.name = name;
  this.age = age;
  this.friends = friends;
   
  //不能使用原型重写的方式来设置方法
  Person.prototype = {
    constructor:Person,
    say:function(){
      console.info(this.name+"["+this.friends+"]");
    }
  }
}

使用動態原型方式創建物件同樣會存在問題,因為類別中的方法是透過Person.prototype.say的方式創建的,這樣每次創建物件的時候,都會在記憶體中創建一個新的say()方法。解決這個問題的方法是我們可以先做一個判斷,看Person.prototype.say方法是否存在,不存在時才創建,否則就不創建。

// 动态原型方式
function Person(name,age,friends){
  this.name = name;
  this.age = age;
  this.friends = friends;
   
  //判断Person.prototype.say是否存在,不存在就创建
  if(!Person.prototype.say){
    alert("创建say方法");
    Person.prototype.say = function(){
      console.info(this.name+"["+this.friends+"]");
    }
  }
}

为了验证判断条件是否起作用,我们在代码中的判断分支中添加了一个弹出对话框语句。我们可以创建2个对象,然后2个对象分别调用say()方法,在结果中,第一个对象在调用say()方法时会弹出对话框,而第二个对象在调用say()方法时就不会在弹出对话框了,也就是说创建第二个对象时不会再添加say()方法。

以上就是JavaScript面向对象-基于组合和动态原型创建对象的内容,更多相关内容请关注PHP中文网(www.php.cn)!


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