Javascript JS定义属性和原型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14047809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 15:33:07  来源:igfitidea点击:

JS defineProperty and prototype

javascriptprototypedefineproperty

提问by nicholas_r

As you know we can define getters and setters in JS using defineProperty(). I've been stuck when trying to extend my class using defineProperty().

如您所知,我们可以使用 .js 在 JS 中定义 getter 和 setter defineProperty()。我在尝试使用defineProperty().

Here is an example code:

这是一个示例代码:

I have an array of fields which must be added to a object

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

fields = ["id", "name", "last_login"]

Also I have a class which will be modified

我还有一个将被修改的类

var User = (function(){
    // constructor
    function User(id, name){
        this.id     = id
        this.name   = name
    }
    return User;
})();

And a function which will add fields to the class using defineProperty()

以及一个将使用字段添加到类的函数 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
            }
        })
    })
};

After running define_fields()I have my fields in the instance of the User

运行后,define_fields()我的字段在User

define_fields(fields);
user1 = new User(1, "Thomas")
user2 = new User(2, "John")

But the values ??of these properties are identical

但是这些属性的值是相同的

console.log(user2.id, user2.name) // 2, John
console.log(user1.id, user1.name) // 2, John

Is there any way to make defineProperty()work properly in this case? If I understand the problem is with valuewhich becomes identical for each instance of the class but i can't realise how to fix it. Thanks in advance for your answers.

defineProperty()在这种情况下有什么方法可以正常工作吗?如果我理解问题是value哪个类的每个实例都相同,但我无法意识到如何解决它。预先感谢您的回答。

UPD:This way throws "RangeError: Maximum call stack size exceeded"

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
            }
        })
    })
};

回答by Totty.js

Please don't implement any other version because it will eat all your memory in your app:

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

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

If 10000 objects are instantiated:

如果实例化了 10000 个对象:

  • With my method: you will only have 2 functions in the memory;
  • With the other methods: 10000 * 2 = 20000 functions in the memory;
  • 用我的方法:你的内存中只有 2 个函数;
  • 使用其他方法:10000 * 2 = 20000 个函数在内存中;

回答by HBP

I came to the same conclusion as Mikhail Kraynovthree minutes after he answered. That solution defines new properties each time the constructor is called. I wondered if, as you asked, there was a way of putting the getters and setters in the prototype. Here is what I came up with:

米哈伊尔·克雷诺夫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;
}) ();  

In this solution I define the field getters and setters in the prototype but reference a (hidden) property in each instance which holds the field values.

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

See the fiddle here : http://jsfiddle.net/Ca7yq

请参阅此处的小提琴:http: //jsfiddle.net/Ca7yq

I added some more code to the fiddle to show some effects on enumeration of properties : http://jsfiddle.net/Ca7yq/1/

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

回答by Mikhail Kraynov

It seems to me, that when you defineProperties for prototype, all instances shares that properties. So the right variant could be

在我看来,当您为原型定义属性时,所有实例都共享该属性。所以正确的变体可能是

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;
})();

回答by Bergi

As you define your properties on the prototypeobject of all user instances, all those objects will share the same valuevariable. If that is not what you want, you will need to call defineFieldson each user instance separately - in the constructor:

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

回答by Andrii Muzalevskyi

This solution is without extra memory consumption. Your updated code is close. You just need to use this.props[field_name] instead of direct this[field_name].

此解决方案没有额外的内存消耗。您更新的代码已关闭。你只需要使用 this.props[field_name] 而不是直接 this[field_name]。

Please note that defineProperty call replaced to Object.create

请注意,defineProperty 调用替换为 Object.create

Js Fiddle http://jsfiddle.net/amuzalevskyi/65hnpad8/

Js小提琴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

回答by Hai Phan

From the accepted answer, I realize what we're trying to do here is defining private instance variables. These variables should be on the instance (this), rather than on the prototype object. Normally we name private variables by prefixing an underscore to the property's name.

从接受的答案中,我意识到我们在这里尝试做的是定义私有实例变量。这些变量应该在实例(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;

The accepted answer basically puts all private variables in a container instead, which is actually better.

接受的答案基本上是将所有私有变量放在一个容器中,这实际上更好。