【问题标题】:when do you use Object.defineProperty()你什么时候使用 Object.defineProperty()
【发布时间】:2012-04-23 18:01:50
【问题描述】:

我想知道什么时候应该使用

Object.defineProperty

为对象创建新属性。我知道我可以设置诸如

enumerable: false

但是你什么时候真的需要这个?如果你只是设置一个属性像

myObject.myprop = 5;

它的描述符都设置为真,对吧?当你们对 .defineProperty() 使用这种相当冗长的调用以及出于什么原因时,我实际上更好奇。

【问题讨论】:

    标签: javascript ecmascript-5


    【解决方案1】:

    我见过的defineProperty 的一个简洁用例是库向用户提供错误属性,如果在特定时间间隔内未访问该属性,您将自己记录错误。例如:

    let logErrorTimeoutId = setTimeout(() => {
      if (error) {
        console.error('Unhandled (in <your library>)', error.stack || error);
      }
    }, 10);
    
    Object.defineProperty(data, 'error', {
        configurable: true,
        enumerable: true,
        get: () => {
          clearTimeout(logErrorTimeoutId);
          return error;
        },
      });
    

    此代码的来源:https://github.com/apollographql/react-apollo/blob/ddd3d8faabf135dca691d20ce8ab0bc24ccc414e/src/graphql.tsx#L510

    【讨论】:

      【解决方案2】:

      例如that's how Vue.js keeps track of changes in the data object:

      当您将纯 JavaScript 对象作为其 data 选项传递给 Vue 实例时,Vue 将遍历其所有属性并使用 Object.defineProperty 将它们转换为 getter/setters。这是一个仅限 ES5 且不可填充的功能,这就是 Vue 不支持 IE8 及更低版本的原因。

      getter/setter 对用户是不可见的,但在底层,它们使 Vue 能够在访问或修改属性时执行依赖关系跟踪和更改通知。

      [...]

      请记住,即使是 Vue.js 的超薄和基本版本也不仅仅使用Object.defineProperty,而是主要功能来自它:

      在这里你可以看到一篇文章,作者实现了类似 Vue.js 的最小 PoC 版本:https://medium.com/js-dojo/understand-vue-reactivity-implementation-step-by-step-599c3d51cd6c

      这里有一个演讲(西班牙语),演讲者在解释 Vue.js 中的反应性时构建了类似的东西:https://www.youtube.com/watch?v=axXwWU-L7RM

      【讨论】:

        【解决方案3】:

        一个非常有用的案例是监控某物的变化并对其采取行动。这很容易,因为您可以在设置值时触发回调函数。这是一个基本示例。

        您有一个对象Player 可以播放或不播放。您希望在开始播放和停止播放时立即发生某些事情。

        function Player(){}
        Object.defineProperty(Player.prototype, 'is_playing', {
          get(){
            return this.stored_is_playing;  // note: this.is_playing would result in an endless loop
          },
          set(newVal){
            this.stored_is_playing = newVal;
            if (newVal === true) {
              showPauseButton();
            } else {
              showPlayButton();
            }
          }
        });
        const cdplayer = new Player();
        cdplayer.is_playing = true; // showPauseButton fires 
        

        此答案与此处的其他几个答案相关,这是获取更多信息的良好步骤,但无需通过外部链接来阅读有关库或编程范例的信息。

        【讨论】:

          【解决方案4】:

          Object.defineProperty 防止您意外地将值分配给原型链中的某个键。使用此方法,您只分配给该特定对象级别(而不是原型链中的任何键)。

          例如: 有一个像{key1: value1, key2: value2} 这样的对象,你不知道它的原型链,或者你错误地错过了它,然后原型链中的某个地方有一些属性“颜色”-

          使用点(.)赋值-

          此操作将为原型链中的 key 'color' 赋值(如果 key 存在于某处),您会发现对象没有任何变化,如 . obj.color='蓝色'; // obj 与 {key1: value1, key2: value2} 保持一致

          使用 Object.defineProperty 方法-

          Object.defineProperty(obj, 'color', {
            value: 'blue'
          });
          

          // 现在 obj 看起来像 {key1: value1, key2: value2, color: 'blue'}。它将属性添加到同一级别。然后您可以使用方法 Object.hasOwnProperty() 安全地迭代。

          【讨论】:

          【解决方案5】:

          总结:

          在 Javascript 中,对象是键值对的集合。 Object.defineProperty() 是一个函数,它可以在对象上定义一个新的属性,并且可以设置一个属性的以下属性:

          • value &lt;any&gt;与键关联的值
          • writable &lt;boolean&gt;: 如果 writable 设置为 true 可以通过为其分配新值来更新该属性。如果设置为 false,则无法更改该值。
          • enumerable &lt;boolean&gt;: 如果 enumerable 设置为 true 属性可以通过 for..in 循环访问。此外,Object.keys() 返回的唯一可枚举属性键
          • configurable &lt;boolean&gt;: 如果可配置设置为 false,则无法更改属性属性(值/可写/可枚举/可配置),因为您无法更改值,因此无法使用delete 运营商。

          示例:

          let obj = {};
          
          
          Object.defineProperty(obj, 'prop1', {
                value: 1,
                writable: false,
                enumerable: false,
                configurable: false
          });   // create a new property (key=prop1, value=1)
          
          
          Object.defineProperty(obj, 'prop2', {
                value: 2,
                writable: true,
                enumerable: true,
                configurable: true
          });  // create a new property (key=prop2, value=2)
          
          
          console.log(obj.prop1, obj.prop2); // both props exists
          
          for(const props in obj) {
            console.log(props);
            // only logs prop2 because writable is true in prop2 and false in prop1
          }
          
          
          obj.prop1 = 100;
          obj.prop2 = 100;
          console.log(obj.prop1, obj.prop2);
          // only prop2 is changed because prop2 is writable, prop1 is not
          
          
          delete obj.prop1;
          delete obj.prop2;
          
          console.log(obj.prop1, obj.prop2);
          // only prop2 is deleted because prop2 is configurable and prop1 is not

          【讨论】:

            【解决方案6】:

            当您需要进行一些拦截或以优雅的方式应用经典的 Observer/Observable 模式时,这是一个很好的用途:

            https://www.monterail.com/blog/2016/how-to-build-a-reactive-engine-in-javascript-part-1-observable-objects

            【讨论】:

            • 检查我的答案,早在 3 月份,t3chfest 就曾有过一次有趣的讨论@?
            【解决方案7】:

            @杰拉德·辛普森

            如果 'area' 应该是可枚举的,它也可以在没有 Object.defineProperty 的情况下编写。

            var myObj = {
                get area() { return this.width * this.height }
            };
            
            myObj.width = 20;
            myObj.height = 20;
            
            for (var key in myObj) {
              if (myObj.hasOwnProperty(key)) {
                console.log(key + " -> " + myObj[key]);
              }
            }
            
            //area -> 400, width -> 20, height -> 20
            

            【讨论】:

              【解决方案8】:

              使用 Object.defineProperty 的一个很好的理由是,它允许您循环遍历对象中的函数作为计算属性,它执行函数而不是返回函数的主体。

              例如:

              var myObj = {};
              
              myObj.width = 20;
              myObj.height = 20;
              
              Object.defineProperty(myObj, 'area', {
                  get: function() {
                      return this.width*this.height;
                  },
                  enumerable: true
              });
              
              for (var key in myObj) {
                if (myObj.hasOwnProperty(key)) {
                  console.log(key + " -> " + myObj[key]);
                }
              }
              //width -> 20, height -> 20, area -> 400
              

              与将函数作为属性添加到对象字面量:

              var myObj = {};
              
              myObj.width = 20;
              myObj.height = 20;
              
              myObj.area = function() {
                     return this.width*this.height;
                  };
              
              for (var key in myObj) {
                if (myObj.hasOwnProperty(key)) {
                  console.log(key + " -> " + myObj[key]);
                }
              }
              // width -> 20, height -> 20, area -> function() { return this.width*this.height;}
              

              确保将 enumerable 属性设置为 true 以便循环遍历它。

              【讨论】:

                【解决方案9】:

                在我的经验中,很少使用像“可枚举”这样的功能。 主要用例是计算属性:

                var myObj = {};
                
                myObj.width = 20;
                myObj.height = 20;
                
                Object.defineProperty(myObj, 'area', {
                    get: function() {
                        return this.width*this.height;
                    }
                });
                console.log(myObj.area);
                

                【讨论】:

                • 在我看来,您不遗余力地以语义上一个非常灰色区域的名义将其作为一个属性;
                • @aaaaaa 什么?灰色地带?
                • 我只是不理解提供计算属性的用例,而不是简单地使其成为一个函数。例如myObj.area = function() { return this.width * this.height; }
                • 该函数完全有效,但在某些用例中,您可能更喜欢属性而不是函数。
                • 主要用途是,您可以使用包装自定义逻辑的 getter / setter 定义单个属性,而不是拥有两个单独的函数(例如:getFoo()、setFoo()),然后只需对单个成员使用标准赋值运算符(例如:x = myObj.Foo 或 myObj.Foo = x)。这使您的代码更简单,并且在您想要防止在序列化为 JSON 时发出值时也很有用。 (例如:你的对象有一堆你不想通过网络发送的特定于 UI 的属性......)总而言之,这有助于 JS 更紧密地遵循标准的 OO 原则
                【解决方案10】:

                Object.defineProperty 主要用于设置具有特定属性描述符的属性(例如只读(常量)、可枚举性(在for (.. in ..) 循环中不显示属性、getter、setter)。

                "use strict";
                var myObj = {}; // Create object
                // Set property (+descriptor)
                Object.defineProperty(myObj, 'myprop', {
                    value: 5,
                    writable: false
                });
                console.log(myObj.myprop);// 5
                myObj.myprop = 1;         // In strict mode: TypeError: myObj.myprop is read-only
                

                示例

                此方法使用属性扩展Object 原型。只定义了getter,可枚举性设置为false

                Object.defineProperty(Object.prototype, '__CLASS__', {
                    get: function() {
                        return Object.prototype.toString.call(this);
                    },
                    enumerable: false // = Default
                });
                Object.keys({});           // []
                console.log([].__CLASS__); // "[object Array]"
                

                【讨论】:

                • 好的,我明白了。但这有意义吗?大多数情况下,我希望所有描述符都设置为 true,所以我可以用旧的方式设置属性,对吗? .defineProperty 还有其他优势吗?
                • @RobW:你也可以不使用.defineProperty创建getterssetters
                • @jAndy 但该属性将在for (.. in ..) 循环中公开:Object.prototype.__defineGetter__('lol',function(){return 3});for(var i in [])alert(i); 显示"lol"Object.defineProperty 可用于定义一个getter/setter, 出现在for (.. in ..) 循环中。
                • @RobW:我更多的是考虑使用var foo = { get lol() { return 5;} };,但问题仍然存在。
                • @Andre:如果你没有看到它们的用例,那么你就不需要它们......当你需要它们时,你会知道;)
                猜你喜欢
                • 2015-10-27
                • 2010-09-19
                • 2018-01-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-05-13
                • 2010-09-23
                相关资源
                最近更新 更多