如您所知,我们可以使用defineProperty()
在JS中定义getter和setter。在尝试使用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
}
})
})
};
答案 0 :(得分:50)
请不要实施任何其他版本,因为它会占用您应用中的所有内存:
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个对象:
答案 1 :(得分:17)
在他回答三分钟后,我得出与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/
答案 2 :(得分:8)
在我看来,当你为原型定义属性时,所有实例都共享该属性。所以正确的变体可能是
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;
})();
答案 3 :(得分:5)
在所有用户实例的 prototype 对象上定义属性时,所有这些对象将共享相同的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;
}
});
});
};
答案 4 :(得分:1)
此解决方案没有额外的内存消耗。您的更新代码已关闭。您只需要使用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
答案 5 :(得分:0)
从接受的答案中,我意识到我们在这里要做的是定义私有实例变量。这些变量应该在实例(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;
接受的答案基本上将所有私有变量放在容器中,这实际上更好。