美文网首页
JS代码题7——属性描述对象

JS代码题7——属性描述对象

作者: twentyshaw | 来源:发表于2019-11-19 14:59 被阅读0次

    数据绑定最基本的实现

    实现一个方法,可以给 obj 所有的属性添加动态绑定事件,当属性值发生变化时会触发事件

    let obj = {
      key_1: 1,
      key_2: 2
    }
    function func(key) {
      console.log(key + ' 的值发生改变:' + this[key]);
    }
    bindData(obj, func);
    obj.key_1 = 2; // 此时自动输出 "key_1 的值发生改变:2"
    obj.key_2 = 1; // 此时自动输出 "key_2 的值发生改变:1"
    

    实现bindData(obj, func)函数

    function bindData(obj, func){
        for(let key in obj){
            let value = obj[key]
            Object.defineProperty(obj,key,{
                set:function(newValue){
                    if (newValue !== value) {
                        this.value = newValue
                        func.call(obj,key)
                    }
                },
                get:function(){
                    return this.value
                }
            })
        }
    }
    

    就这么点代码折磨了我一个中午
    踩过的几个坑:
    1. 在遍历对象属性的时候用for(var key in obj)
    这个地方是不能用var的,因为会导致你不管调用 obj.key_1还是obj.key_2他都只能打印出:


    var声明的那个key会保留在上层作用域,你在调用 obj.key_1触发func.call(obj,key)的时候,里面的key为循环遍历后最后赋给key的值,也就是本题中的key_2

    2. this.value在赋值以前为undefined
    我的第一版代码是这么写的(是错的)

    function bindData(obj, func) {
      for (let key in obj) {
        Object.defineProperty(obj, key, {
          set: function(newValue) {
            if (this.value !== newValue) {
              this.value = newValue;
              func.call(obj, key);
            }
          },
          get: function(){
            return this.value;
          }
        })
      }
    }
    

    这种写法会导致if (this.value !== newValue)判断失效。因为这时的this.value为undefined,所以无论属性值是否发生变化,func.call(obj,key)都会被调用,如:


    这显然不符合题目要求的“当属性值发生变化时会触发事件”
    针对这个问题,需要学习一下属性描述对象Object.defineProperty()

    属性描述对象

    来自MDN的定义

    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象

    这个方法接受三个参数:

    • obj:要在其上定义属性的对象。
    • prop:要定义或修改的属性的名称。
    • descriptor:将被定义或修改的属性描述符。

    属性描述符:descriptor

    属性描述符分为两种形式:数据描述符存取描述符
    描述符必须是这两种形式之一;不能同时是两者。

    • 数据描述符:一个具有值的属性,该值可能是可写的,也可能不是可写的。
      它具有以下可选键值:
      value:该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
      writable:当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。

    • 存取描述符:由getter-setter函数对描述的属性。
      它具有以下可选键值:
      get:取值函数。在对象调用属性的时候触发。
      set:存值函数。在给属性赋值的时候触发。

    以上这四个属性中,数据描述符的属性不能和存取描述符的属性同时存在:

    如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。

    除此之外,还有两个共同的属性:

    • configurable:当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
    • enumerable:当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。

    以上为基本概念,结合这些基本概念本题中要注意的点有:

    • Object.defineProperty()方法中,this指向定义该属性的对象(不考虑继承关系)
    • Object.defineProperty()修改属性之后,比如
    var obj = {
      key: 1,
    }
    Object.defineProperty(obj,'key',{
        set:function(newValue){
                this.value = newValue
        },
        get:function(){
                return this.value
        }
    })
    

    这时如果调用obj.key返回的值为undefined:


    因为obj.key会触发取值函数,但取值函数返回的this.value为undefined。也就是说:
    修改属性的属性描述符过后,之前的属性值就不存在了。该属性是一个全新的属性。它的值取决于value(数据描述符)或者get函数的返回值(存取描述符)
    但是上面的例子,只要修改过后,给属性赋一次值,this.value就有值了(不为undefined)。

    相关文章

      网友评论

          本文标题:JS代码题7——属性描述对象

          本文链接:https://www.haomeiwen.com/subject/rsvwictx.html