ご存じのように、defineProperty()
を使用してJSでゲッターとセッターを定義できます。 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
にあることを理解していても、それを修正する方法を理解できません。答えてくれてありがとう。
PD:この方法では、「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 と答えた3分後に同じ結論に達しました。このソリューションは、コンストラクターが呼び出されるたびに新しいプロパティを定義します。あなたが尋ねたように、ゲッターとセッターをプロトタイプに入れる方法はあるのだろうかと思いました。ここに私が思いついたものがあります:
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;
}) ();
このソリューションでは、プロトタイプでフィールドゲッターとセッターを定義しますが、フィールド値を保持する各インスタンスの(隠し)プロパティを参照します。
ここでフィドルを参照してください: 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;
})();
すべてのユーザーインスタンスの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;
}
});
});
};
このソリューションには、余分なメモリ消費はありません。更新されたコードは近いです。直接this [field_name]の代わりにthis.props [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;
受け入れられた答えは、基本的にすべてのプライベート変数を代わりにコンテナに入れますが、これは実際には優れています。