• javascript的属性描述符


    什么是属性描述对象(attributes object)?

    顾名思义,就是用来描述对象属性的对象.javascript内部提供了一个数据结构,用来描述对象的属性以及控制属性的行为.
    比如该对象的某属性是否可写,可遍历等

    属性描述符的6个元属性

    1. value 该属性的值,默认是undefined
    2. configurable 表示可配置,默认为true;如果改为false,则无法删除该属性并且无法改变该属性的属性描述符对象(除了value值)
    3. writable 表示该属性值是否可改变,默认为true;如果改为false,则属性值不能改变
    4. enumerable 该属性是否可遍历,如果设置为false,则object.keys和for...in 无法遍历到该属性
    5. get 获取该属性值是会被调用
    6. set 设置该属性值是会被调用

    获取属性描述对象

    Object.getOwnPropertyDescriptor(对象obj,指定属性);
    两个参数,第一个参数是对象,第二个参数是想要获取属性描述符的属性(类型是字符串)
    默认情况下,configurable,enumerable,writable都是true
    通过Object.defineProperty(对象,属性,{});这样添加的属性,此属性的属性描述符configurable,enumable,writable默认都为false

    let obj = {
        a:1,
        b:2
    }
    console.dir(Ojbect.getOwnPropertyDescriptor(obj, 'a'));
    // 输出
    // {configurable: true,
    // enumerable: true,
    // value: 1,
    // writable: true}
    
    Object.defineProperty(obj,'c',{});
    console.dir(Object.defineOwnPropertyDescriptor(obj,'c'));
    // 输出
    // {configurable: false,
    // enumerable: false,
    // value: undefined,
    // writable: false}
    

    注意: Object.getOwnPropertyDescriptor(obj, 属性); 只能用在对象自身的属性,不能用于继承过来的属性

    获取对象自身的属性名

    1. Object.keys(obj)
    2. Object.getOwnPropertyNames(obj)

    区别,Object.keys(obj)返回对象自身可枚举的属性;Object.getOwnPropertyNames(obj)会返回对象自身的全部属性,不管是否可遍历

    // 声明定义对象
    let obj = {
        a: 1,
        b: 2
    }
    // 给对象obj添加一个属性c,并且属性c不可枚举(不可遍历)
    Object.defineProperty(obj, 'c', {
        enumerable: false
    });
    // 输出对象obj所有键
    console.log(Object.keys(obj));
    console.log(Object.getOwnPropertyNames(obj));
    // 可以看出Object.keys()可以吧对象自身的所有不可枚举的属性打印出来
    // Object.getOwnPropertyNames()可以吧对象自身的所有属性都可以打印出来
    

    定义属性描述对象

    可以使用Object.defineProperty(obj, propertyName, attrObject);来定义一个属性描述对象
    用法,三个参数:

    1. obj 要定义的对象
    2. propertyName 属性名(如果对象上没有此属性则新加此属性,如果对象上有此属性则更新此属性的属性描述对象)
    3. attrObject 定义的对象上的属性描述符对象

    如果一次性修改或定义多个属性的属性描述符对象,则可以使用Object.defineProperties(obj, { key1: key1AttrbuteObject, ... });

    注意: 如果属性描述符定义了存取器(get,set),就不能再设置writable,或者value,否则会报错

    let obj = {
        a: 1
    }
    Object.defineProperty(obj, 'a', {
        // 情况一
        // writable: false,
        // 情况二
        // wirtable: true,
        // 情况三
        value: 123,
        get: function(){
            console.log('get方法');
        }
    });
    // 以上情况都会报错
    // Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
    

    判断某个属性是否可以枚举(可遍历)

    使用propertyIsEnumerable(属性名)可以判断该属性是否可以枚举,propertyIsEnumerable(属性名)是定义在实例对象上的,也就是Object.prototype.propertyIsEnumerabl(),只要是Object的实例都可以访问
    propertyIsEnumerable()只能判断自身的属性,如果判断的是继承过来的属性或者自身不存在的属性,则直接返回false

    let obj = {
        b: 2
    }
    Object.defineProperty(obj, 'a', {
        enumerable:false
    });
    // 自身可枚举的属性 true
    console.log(obj.propertyIsEnumerable('b'));
    // 自身不可枚举的属性 false
    console.log(obj.propertyIsEnumerable('a'));
    // 继承过来的属性 false
    console.log(obj.propertyIsEnumerable('toString'));
    // 自身不存在的属性 false
    console.log(obj.propertyIsEnumerable('c'));
    

    属性描述符的元属性

    1. writable
      决定该属性是否可以改变值,false不可以改变;true可以改变(不能与get/set同时存在)
      如果writable为false,再改变值的话还是之前的值;如果是严格模式会报错

    2. enumerable
      决定该属性是否可以枚举
      enumerable为false时,以下三个不会取到该属性

      • for...in...
      • Object.keys()
      • JSON.stringify()
      let obj = {
       	a: 1,
       }
       Object.defineProperty(obj, 'a', {
       	value: 123,
       	enumerable: false
       });
       // 123
       console.log(obj.a);
       // []
       console.log(Object.keys(obj));
       // {}
       console.log(JSON.stringify(obj));
      
    3. value
      读取或者改写属性的值

      let obj = {
          a: 1
      }
      // 读取值
      console.log(Object.getOwnPropertyDescriptor(obj,'a').value);
      // 改写值
      Object.defineProperty(obj, 'a', {vaue: 123});
      
    4. configurable
      configurable决定了属性的可配置性,当configurable为false时,valueenumerablewritableconfigurable都不可以修改配置,并且不能使用delete删除该属性

    5. get

    6. set
      也可以称为存取器
      两种写法:
      一,Object.defineProperty(obj,'a',{get: function(){ }})
      二,var obj = {get p(){},set p(val){}}

      var obj = Object.defineProperty({}, 'a', {
          get: function(){
              return 'getter';
          },
          set: function(){
              console.log('a的set方法');
          }
      });
      
      var obj1 = {
          get a(){
              return 'getter'
          },
          set a(){
              console.log('a的set方法');
          }
      }
      

    对象的拷贝

    浅拷贝

    1. 直接赋值引用
      问题是改变一个对象的属性值会影响到另一个
      let obj = {a:1}
      let objC = obj;
      objC.a = 2;
      // 因为obj和objC指向同一个内存地址,所以改变objC必然会影响到obj
      
    2. 将对象的每个属性及属性值赋给拷贝对象
      问题是如果被拷贝的对象的某一属性值是对象的话,拷贝的还是引用,还是会相互影响的
      // 被拷贝的对象
      let obj = {a:1, b: { c:2 }}
      let objC = {}
      for(let key in obj){
           if(!obj.hasOwnProperty(key))continue;
           objC[key] = obj[key]
      }
      objC.b.c = 3;
      // 因为objC和obj的属性b都指向同一个地址,所以会相互影响
      
      以上的方式不能够拷贝属性的存取器
      let obj = {get a(){return 1},get b(){return 2}}
      let objC = {}
      for(let key in obj){
           // 因为Object.getOwnPropertyDescriptor()只能获取自身的,获取不到继承的属性,会报错
          if(!obj.hasOwnProperty(key))continue;
          Object.defineProperty(objC, key, Object.getOwnPropertyDescriptor(obj, key));
      }
      

    深拷贝

    function extend(to,from){
         for(let fr in from){
         	if(!from.hasOwnProperty(fr))continue;
         	if(typeof from[fr] === 'object'){
         		if(Object.prototype.toString.call(from[fr]) === '[object Object]'){
         			to[fr] = {};
         			extend(to[fr], from[fr]);
         			// console.log(Object.prototype.toString.call(from[fr]));
         		}else if(Object.prototype.toString.call(from[fr]) === '[object Array]'){
         			to[fr] = [];
         			for(let i=0;i<from[fr].length;i++){
         				to[fr][i] = from[fr][i]
         			}
         		}
         	}else{
         		to[fr] = from[fr];
         	}
         }
         return objC;
    }
    

    控制对象的状态

    冻结对象的读写状态,防止对象被改变

    1. Object.preventExtension(obj);
      // 阻止对象添加属性
      // 判断对象是否可以被扩展
      Object.isExtensible(obj)
    2. Object.seal(obj)
      // 阻止对象添加属性和删除旧属性
      // 判断是否使用了seal方法
      Object.isSealed(obj)
      使用了Object.seal(obj)之后,Object.isExtensible(obj)也变为false
    3. Object.freeze(obj)
      // 使得对象扩展属性,删除属性,并且值也不能改变,变成了常量
      // 判断对象是否用了freeze方法
      Object.isFrozen(obj)
      使用了Object.freeze(obj)之后,Object.isSealed(obj)返回true,Object.isExtensible(obj)返回false
  • 相关阅读:
    常见正则总结
    word 操作教程
    word调整技巧
    关于如何自定义handler
    html 处理
    iis 导入和导出配置——iis管理
    前端学习
    动态添加js的方法
    jquery学习笔记
    php学习笔记
  • 原文地址:https://www.cnblogs.com/li320212035/p/12944508.html
Copyright © 2020-2023  润新知