JS defineProperty和原型

nic*_*s_r 34 javascript prototype defineproperty

如您所知,我们可以使用JS定义getter和setter defineProperty().我试图扩展我的班级时遇到困难defineProperty().

这是一个示例代码:

我有一个必须添加到对象的字段数组

fields = ["id", "name", "last_login"]
Run Code Online (Sandbox Code Playgroud)

我还有一个将被修改的课程

var User = (function(){
    // constructor
    function User(id, name){
        this.id     = id
        this.name   = name
    }
    return User;
})();
Run Code Online (Sandbox Code Playgroud)

还有一个函数,它将使用类添加字段 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
            }
        })
    })
};
Run Code Online (Sandbox Code Playgroud)

运行后define_fields()我在我的实例中有我的字段User

define_fields(fields);
user1 = new User(1, "Thomas")
user2 = new User(2, "John")
Run Code Online (Sandbox Code Playgroud)

但这些属性的值是相同的

console.log(user2.id, user2.name) // 2, John
console.log(user1.id, user1.name) // 2, John
Run Code Online (Sandbox Code Playgroud)

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
            }
        })
    })
};
Run Code Online (Sandbox Code Playgroud)

Tot*_*.js 52

请不要实现任何其他版本,因为它会占用您应用中的所有内存:

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
Run Code Online (Sandbox Code Playgroud)

如果您实例10000对象:

  • 用我的方法:你在内存中只有2个函数;
  • 使用其他方法:10000*2 = 20000个内存函数;

  • 什么是这个语法?!为什么我从未见过这个?! (11认同)

HBP*_*HBP 18

在他回答三分钟后,我得出了与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;
}) ();  
Run Code Online (Sandbox Code Playgroud)

在这个解决方案中,我在原型中定义字段getter和setter,但在每个保存字段值的实例中引用(隐藏)属性.

请看这里的小提琴:http://jsfiddle.net/Ca7yq

我在小提琴中添加了一些代码,以显示对枚举属性的一些影响:http://jsfiddle.net/Ca7yq/1/

  • 好方案!我只是想补充一点,你可以添加`User.prototype.toJSON = function(){return this .__; 在``User`构造函数内部,这样你就可以直接`JSON.stringify(user1)`而不是`JSON.stringify(user1 .__)`.如果您使用的是对象进行字符串化的持久层,那么您可以将对象传递给save方法! (3认同)

Mik*_*nov 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;
})();
Run Code Online (Sandbox Code Playgroud)

  • 难道这不会占用你所有的记忆吗?它将为每个实例创建新功能. (3认同)
  • 是的,我想到了这个解决方案.但它会带来一些其他问题.假设我有一个抽象的"Model"类和一个继承"Model"的`User`.所以我需要一些能够自动为`User`类创建所需方法的东西.每次创建新模型时,我都不想要这些方法的写定义.例如,不久之后,我将有"Album"和"Song"类,它们也将继承"Model". (2认同)

Ber*_*rgi 5

在所有用户实例的原型对象上定义属性时,所有这些对象将共享相同的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;
            }
        });
    });
};
Run Code Online (Sandbox Code Playgroud)