[英]JS defineProperty and prototype
如您所知,我們可以使用defineProperty()
在JS中定義getter和setter。 我在嘗試使用defineProperty()
擴展我的類時defineProperty()
。
這是一個示例代碼:
我有一個必須添加到對象的字段數組
fields = ["id", "name", "last_login"]
我還有一個將被修改的課程
var User = (function(){
// constructor
function User(id, name){
this.id = id
this.name = name
}
return User;
})();
還有一個函數,它將使用defineProperty()
向類添加字段
var define_fields = function (fields){
fields.forEach(function(field_name){
var value = null
Object.defineProperty(User.prototype, field_name, {
get: function(){ return value }
set: function(new_value){
/* some business logic goes here */
value = new_value
}
})
})
};
運行define_fields()
我在User
的實例中有我的字段
define_fields(fields);
user1 = new User(1, "Thomas")
user2 = new User(2, "John")
但這些屬性的值是相同的
console.log(user2.id, user2.name) // 2, John
console.log(user1.id, user1.name) // 2, John
有沒有辦法讓defineProperty()
在這種情況下正常工作? 如果我理解問題的value
是對於每個類的實例變得相同但我無法意識到如何解決它。 提前感謝您的回答。
UPD:這種方式拋出“RangeError:超出最大調用堆棧大小”
var define_fields = function (fields){
fields.forEach(function(field_name){
Object.defineProperty(User.prototype, field_name, {
get: function(){ return this[field_name] }
set: function(new_value){
/* some business logic goes here */
this[field_name] = new_value
}
})
})
};
請不要實現任何其他版本,因為它會占用您應用中的所有內存:
var Player = function(){this.__gold = 0};
Player.prototype = {
get gold(){
return this.__gold * 2;
},
set gold(gold){
this.__gold = gold;
},
};
var p = new Player();
p.gold = 2;
alert(p.gold); // 4
如果您實例10000對象:
在他回答三分鍾后,我得出了與Mikhail Kraynov相同的結論。 每次調用構造函數時,該解決方案都會定義新屬性。 我想知道,如你所說,是否有一種方法可以將吸氣劑和制定者放入原型中。 這是我想出的:
var User = (function () {
function User (id, nam) {
Object.defineProperty (this, '__', // Define property for field values
{ value: {} });
this.id = id;
this.nam = nam;
}
(function define_fields (fields){
fields.forEach (function (field_name) {
Object.defineProperty (User.prototype, field_name, {
get: function () { return this.__ [field_name]; },
set: function (new_value) {
// some business logic goes here
this.__[field_name] = new_value;
}
});
});
}) (fields);
return User;
}) ();
在這個解決方案中,我在原型中定義字段getter和setter,但在每個保存字段值的實例中引用(隱藏)屬性。
請看這里的小提琴: http : //jsfiddle.net/Ca7yq
我在小提琴中添加了一些代碼,以顯示對枚舉屬性的一些影響: http : //jsfiddle.net/Ca7yq/1/
在我看來,當您為原型定義屬性時,所有實例都共享該屬性。 所以正確的變體可能是
var User = (function(){
// constructor
function User(id, name){
this.id = id
this.name = name
Object.defineProperty(this, "name", {
get: function(){ return name },
set: function(new_value){
//Some business logic, upperCase, for example
new_value = new_value.toUpperCase();
name = new_value
}
})
}
return User;
})();
在所有用戶實例的原型對象上定義屬性時,所有這些對象將共享相同的value
變量。 如果那不是你想要的,你需要在每個用戶實例上單獨調用defineFields
- 在構造函數中:
function User(id, name){
this.define_fields(["name", "id"]);
this.id = id
this.name = name
}
User.prototype.define_fields = function(fields) {
var user = this;
fields.forEach(function(field_name) {
var value;
Object.defineProperty(user, field_name, {
get: function(){ return value; },
set: function(new_value){
/* some business logic goes here */
value = new_value;
}
});
});
};
此解決方案沒有額外的內存消耗。 您的更新代碼已關閉。 您只需要使用this.props [field_name]而不是直接使用[field_name]。
請注意,defineProperty調用已替換為Object.create
Js Fiddle http://jsfiddle.net/amuzalevskyi/65hnpad8/
// util
function createFieldDeclaration(fields) {
var decl = {};
for (var i = 0; i < fields.length; i++) {
(function(fieldName) {
decl[fieldName] = {
get: function () {
return this.props[fieldName];
},
set: function (value) {
this.props[fieldName] = value;
}
}
})(fields[i]);
}
return decl;
}
// class definition
function User(id, name) {
this.props = {};
this.id = id;
this.name = name;
}
User.prototype = Object.create(Object.prototype, createFieldDeclaration(['id','name']));
// tests
var Alex = new User(0, 'Alex'),
Andrey = new User(1, 'Andrey');
document.write(Alex.name + '<br/>'); // Alex
document.write(Andrey.name + '<br/>'); // Andrey
Alex.name = "Alexander";
document.write(Alex.name + '<br/>'); // Alexander
document.write(Andrey.name + '<br/>'); //Andrey
從接受的答案中,我意識到我們在這里要做的是定義私有實例變量 。 這些變量應該在實例(this)上,而不是在原型對象上。 通常我們通過在下划線前加上屬性的名稱來命名私有變量。
var Vehicle = {};
Object.defineProperty(Vehicle, "make", {
get: function() { return this._make; }
set: function(value) { this._make = value; }
});
function Car(m) { this.make = m; } //this will set the private var _make
Car.prototype = Vehicle;
接受的答案基本上將所有私有變量放在容器中,這實際上更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.