JS定义属性和原型
JS defineProperty and prototype
正如你所知道的,我们可以使用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
成为相同的每个实例的类,但我不知道如何解决它。提前感谢
乌利希期刊指南:这种方法抛出"RangeError: Maximum call stack size exceeded"
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个对象:
- 使用我的方法:您将在内存中只有2个函数;
- 配合其他方法:10000 * 2 = 20000个函数在内存中;
在他回答三分钟后,我得出了和Mikhail Kraynov相同的结论。该解决方案在每次调用构造函数时定义新的属性。我想知道,是否如你所问的,有一种方法把getter和setter放到原型中。下面是我想到的:
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;
}
});
});
};
此解决方案不需要额外的内存消耗。更新后的代码很接近。你只需要用这个。Props [field_name]代替direct this[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;
被接受的答案基本上是将所有私有变量放在容器中,这实际上更好。
- 附加到原型属性的Do函数没有闭包
- 日期原型属性
- JS构造函数的原型属性与其原型之间的区别
- 为什么函数对象的实例没有继承函数原型属性
- 为什么浏览器显示原型属性不同
- 为什么在Function.prototype上没有原型属性
- 函数的原型属性
- 原型继承和原型属性
- 在JavaScript中,函数的默认值是多少'的原型属性
- Chrome 不支持 Javascript 中的原型属性吗?
- 我应该在JavaScript中将什么连接到子原型属性
- 更新 JavaScript 中的原型属性
- 请解释有关 JavaScript 中的原型属性和函数构造函数的详细信息
- 为什么原型函数无法读取原型属性
- 与对象属性同名的原型属性
- 如何获取原型属性列表
- 构造函数中的方法与函数的原型属性之间的差异
- JavaScript 原型属性
- 函数对象__proto__和原型属性
- 未在自定义 OL3 控件的构造函数中定义的原型属性