2015-06-19 53 views
0

直到今天我才知道這個問題,我想知道繞過它的最好方法是什麼。問題是,當你從相同的構造函數實例化兩個不同的對象時,它們共享相同的原型,這意味着如果一個對象設置了原型,所有其他對象也將被改變。在JS對象的不同實例之間正確隔離數據

例如:

function A(obj) { 

} 

A.prototype = { 

    events: { 
     one: 1 
    } 

}; 


var b = new A(); 

console.log(b.events); 

var c = new A(); 

console.log(c.events); 

b.events.x = 2; 

console.log(b.events); 
console.log(c.events); //whoops, c also got b's event x 

究竟發生了什麼?解決這個問題的最佳方法是什麼?

這是我想出的,但我想知道是否有更好的方法?

var _ = require('underscore'); 

function A(obj) { 
    if (obj == null) { 
     obj = {}; 
    } 
    if(obj.events == null){ 
     obj.events = {}; 
    } 
    this.events = _.extend(obj.events, A.prototype.events); 
} 

A.prototype = { 

    events: { 
     one: 1 
    } 

}; 


var b = new A({events:{three:3}}); 

console.log(b.events); 

var c = new A({events:{four:4}}); 

console.log(c.events); 

b.events.x = 2; 

console.log(b.events); 
console.log(c.events); //now it's better...(this is crazy) 

回答

1

正如您發現的,原型是在給定對象的所有實例之間共享的。這是由於幾個原因,包括存儲效率。因此,您應該只將事物置於原型中,以便在所有實例中相同地共享。

如果您希望某個實例變量對每個單獨實例具有唯一值,那麼它不屬於原型。它屬於一個實例變量,通過this指針設置。因此,如果您的event的屬性旨在成爲對每個實例都可能不同的實例變量,那麼在構造函數中初始化它,並且它對每個單獨的實例都是唯一的。

function A(obj) { 
    this.events = {one: 1}; 
} 

並將其從原型中刪除。請記住,原型是針對給定類型對象的所有實例之間相同共享的東西而設計的。這就是爲什麼它是完美的方法,很少用於可修改的數據。

工作的代碼示例:

function A() { 
 
    this.events = {one: 1}; 
 
} 
 

 
var x = new A(); 
 
var y = new A(); 
 

 
log(x.events); 
 
log(y.events); 
 
log("--------------"); 
 

 
x.events.one = 2; 
 
y.events.one = 3; 
 

 
log(x.events); 
 
log(y.events); 
 

 
function log(x) { 
 
    var d = document.createElement("div"); 
 
    if (typeof x === "object") { 
 
     x = JSON.stringify(x); 
 
    } 
 
    d.textContent = x; 
 
    document.body.appendChild(d); 
 
}

1

爲什麼這麼複雜?

function A(obj) { 
    this.events = { 
     one: 1 
    }; 
} 
1

你不應該保存變量您的原型,而不是在構造函數使用。 在你的情況下,解決辦法是:

function A(obj) { 
     this.events = { 
      one: 1 
     }; 
    } 


    var b = new A(); 

    console.log(b.events); 

    var c = new A(); 

    console.log(c.events); 

    b.events.x = 2; 

    console.log(b.events); 
    console.log(c.events); 
1

的第一步是學習原型繼承。

所有原型繼承是一個活生生的參照值...

而在幕後,

A.prototype.method = function foo() { }; 

var a = new A(); 
a.__proto__.method === a.constructor.prototype.method; 
a.__proto__ === A.prototype; 

,除非你改變.prototype的'價值」是一個新的對象......然後你的舊實例將指向舊的原型對象,並且你的新實例將指向新的原型對象。

這是基本的深拷貝與淺拷貝,值與參考的東西。 除了通過閉包(它看起來不像Java/C#)之外,沒有私有變量的Java/C#繼承。

如果你打算把東西放在一個原型上,它們應該只是方法和常量/枚舉,它們對於每個實例都是靜態可用的(因爲在實例出現的時候生效參考.constructor.prototype的值製作)。

真的,除非你在硬核內存池優化模式下,直到ES6類可用(人們瞭解他們---他們只是圍繞.prototype),這是比它的價值更精神上的麻煩,並且經常(雖然不總是)更簡單地構建你需要的東西,或者使用mixins,而不是針對看起來像Java的JS(但表現得非常不同)。