美文网首页
JS中的属性描述对象 2

JS中的属性描述对象 2

作者: 诗和远方何你 | 来源:发表于2018-05-04 15:40 被阅读0次

    元属性

    属性描述对象的属性,被称为“元属性”,因为它可以看作是控制属性的属性。

    可枚举性(enumerable)

    JavaScript的最初版本,in 运算符和基于它的for...in循环,会遍历对象实例的所有属性,包括继承的属性。

    var obj = {};
    'toString' in obj // true
    

    上面代码中,toString不是obj对象自身的属性,但是in运算符也返回true,导致被for...in循环遍历,这显然不太合理。后来就引入了“可枚举性”这个概念,只有可枚举的属性,才会被for...in循环遍历,同时还规定原生继承的属性都是不可枚举的,这样就保证了for...in循环的可用性。

    可枚举性(enumerable)用来控制所描述的属性,是否将被包括在for...in循环之中。具体来说,如果一个属性的enumerable为false,下面三个操作不会取到该属性。

    for..in循环
    Object.keys方法
    JSON.stringify方法
    因此,enumerable可以用来设置“秘密”属性。

    var o = {a: 1, b: 2};
    
    o.c = 3;
    Object.defineProperty(o, 'd', {
      value: 4,
      enumerable: false
    });
    
    o.d // 4
    
    for (var key in o) {
      console.log(o[key]);
    }
    // 1
    // 2
    // 3
    
    Object.keys(o)  // ["a", "b", "c"]
    
    JSON.stringify(o) // "{a:1, b:2, c:3}"
    

    上面代码中,d属性的enumerable为false,所以一般的遍历操作都无法获取该属性,使得它有点像“秘密”属性,但不是真正的私有属性,还是可以直接获取它的值。

    基本上,JavaScript原生提供的属性都是不可枚举的,用户自定义的属性都是可枚举的。

    与枚举性相关的几个操作的区别的是,for...in循环包括继承自原型对象的属性,Object.keys方法只返回对象本身的属性。如果需要获取对象自身的所有属性,不管是否可枚举,可以使用Object.getOwnPropertyNames方法,详见下文。

    考虑到JSON.stringify方法会排除enumerable为false的值,有时可以利用这一点,为对象添加注释信息。

    var car = {
      id: 123,
      color: 'red',
      ownerId: 12
    };
    
    var owner = {
      id: 12,
      name: 'Jack'
    };
    
    Object.defineProperty(car, 'ownerInfo', {
      value: owner,
      enumerable: false
    });
    
    car.ownerInfo
    // {id: 12, name: "Jack"}
    
    JSON.stringify(car)
    //  "{"id": 123,"color": "red","ownerId": 12}"
    

    上面代码中,owner对象作为注释部分,加入car对象。由于ownerInfo属性不可枚举,所以JSON.stringify方法最后输出car对象时,会忽略ownerInfo属性。

    这提示我们,如果你不愿意某些属性出现在JSON输出之中,可以把它的enumerable属性设为false。

    可配置性(configurable)

    可配置性(configurable)决定了是否可以修改属性描述对象。也就是说,当configurable为false的时候,value、writable、enumerable和configurable都不能被修改了。

    var o = Object.defineProperty({}, 'p', {
      value: 1,
      writable: false,
      enumerable: false,
      configurable: false
    });
    
    Object.defineProperty(o,'p', {value: 2})
    // TypeError: Cannot redefine property: p
    
    Object.defineProperty(o,'p', {writable: true})
    // TypeError: Cannot redefine property: p
    
    Object.defineProperty(o,'p', {enumerable: true})
    // TypeError: Cannot redefine property: p
    
    Object.defineProperties(o,'p',{configurable: true})
    // TypeError: Cannot redefine property: p
    

    上面代码首先定义对象o,并且定义o的属性p的configurable为false。然后,逐一改动value、writable、enumerable、configurable,结果都报错。

    需要注意的是,writable只有在从false改为true会报错,从true改为false则是允许的。

    var o = Object.defineProperty({}, 'p', {
      writable: true,
      configurable: false
    });
    
    Object.defineProperty(o,'p', {writable: false})
    // 修改成功
    

    至于value,只要writable和configurable有一个为true,就允许改动。

    var o1 = Object.defineProperty({}, 'p', {
      value: 1,
      writable: true,
      configurable: false
    });
    
    Object.defineProperty(o1,'p', {value: 2})
    // 修改成功
    
    var o2 = Object.defineProperty({}, 'p', {
      value: 1,
      writable: false,
      configurable: true
    });
    
    Object.defineProperty(o2,'p', {value: 2})
    

    另外,configurable为false时,直接对该属性赋值,不报错,但不会成功。

    var o = Object.defineProperty({}, 'p', {
      value: 1,
      configurable: false
    });
    
    o.p = 2;
    o.p // 1
    

    上面代码中,o对象的p属性是不可配置的,对它赋值是不会生效的。

    可配置性决定了一个变量是否可以被删除(delete)。

    var o = Object.defineProperties({}, {
      p1: { value: 1, configurable: true },
      p2: { value: 2, configurable: false }
    });
    
    delete o.p1 // true
    delete o.p2 // false
    
    o.p1 // undefined
    o.p2 // 2
    

    上面代码中的对象o有两个属性,p1是可配置的,p2是不可配置的。结果,p2就无法删除。

    需要注意的是,当使用var命令声明变量时,变量的configurable为false。

    var a1 = 1;
    
    Object.getOwnPropertyDescriptor(this,'a1')
    // Object {
    //  value: 1,
    //  writable: true,
    //  enumerable: true,
    //  configurable: false
    // }
    

    而不使用var命令声明变量时(或者使用属性赋值的方式声明变量),变量的可配置性为true。

    a2 = 1;
    
    Object.getOwnPropertyDescriptor(this,'a2')
    // Object {
    //  value: 1,
    //  writable: true,
    //  enumerable: true,
    //  configurable: true
    // }
    
    // 或者写成
    
    window.a3 = 1;
    
    Object.getOwnPropertyDescriptor(window, 'a3')
    // Object {
    //  value: 1,
    //  writable: true,
    //  enumerable: true,
    //  configurable: true
    // }
    

    上面代码中的this.a3 = 1与a3 = 1是等价的写法。window指的是浏览器的顶层对象。

    这种差异意味着,如果一个变量是使用var命令生成的,就无法用delete命令删除。也就是说,delete只能删除对象的属性。

    var a1 = 1;
    a2 = 1;
    
    delete a1 // false
    delete a2 // true
    
    a1 // 1
    a2 // ReferenceError: a2 is not defined
    

    相关文章

      网友评论

          本文标题:JS中的属性描述对象 2

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