美文网首页javascript
Object.defineProperty

Object.defineProperty

作者: YINdevelop | 来源:发表于2021-04-25 15:51 被阅读0次

    在了解什么是Object.defineProperty前,我们先回忆下我们平常经常使用的 对象

    1.对象的赋值

    我们平常一般使用obj.prop=value或者obj['prop']=value对对象进行赋值或修改,如

    let test={}
    test.a=1
    test["b"]=2
    console.log(test.a) //1
    console.log(test.b) //2
    

    那么Object.defineProperty又和上面的对象赋值是什么关系呢?从字面(defineProperty)意思上就可以知道--定义属性,所以Object.defineProperty也是一种对对象属性修改或赋值的方式,只不过我们可以进行更精确的控制.

    2.Object.defineProperty的定义:

    Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,语法如下,有三个参数:

    Object.defineProperty(obj, prop, descriptor)
    
    • obj:需要定义或修改属性的对象(必填)
    • prop:需要定义的属性名称(必填)
    • descriptor:要定义或修改的属性描述符,是一个对象.(必填)

    看到这,可能有的人不理解什么是属性描述符号?我们上面说的 对对象属性进行更精确的控制,就是通过descriptor来操控的.descriptor是一个对象,具有以下属性:

    属性名 描述 默认值
    value 要定义的属性对应的值 undefined
    writable 要定义的属性值是否可以被改变 false
    configurable 要定义的属性是否配置(配置指的是重新定义),以及可否删除 false
    enumerable 要定义的属性是否会出现在对象的枚举属性中,如for in 或者 Object.keys()的遍历中 false
    get 要定义的属性的getter函数,当访问该属性时,会执行此函数 undefined
    set 要定义的属性的setter函数,当属性值被修改时,会调用此函数,此时该方法接受一个参数(也就是修改的新值) undefined

    注意:这里说的要定义的属性指的是通过Object.defineProperty传递prop参数进行定义的属性(才可以控制是否删除、改变、监听等),如果对象本来就存在一些属性,但没有经过Object.defineProperty进行重新定义,是不受控制的,和我们最开始讲到的我们平常使用对象的方式,可以随便改变、删除,不能监听
    下面我们将分别对这些属性描述符进行举例说明:

    1.value

    我们要在对象上定义属性,obj、prop必填就不用说了,descriptor也是必填的,否则会报错

    let test={}
    Object.defineProperty(test, "a")
    console.log(test);
    
    image.png

    从报错信息我们可知,属性描述符必须是个对象,所以我们修改下就可以了

    Object.defineProperty(test, "a",{})
    console.log(test); // {a:undefined}
    test.a=1
    console.log(`修改后${test}`) // {a:undefined}
    

    此时我们已经使用Object.defineProperty成功定义了一个属性,只不过没有值罢了.
    但我们修改,打印出来发现值仍为undefined.这个后面会说,接下来我们先进行使用 value进行赋值操作.

      Object.defineProperty(test, "a",{
          value:1
      })
      console.log(test); // {a:1}
      test.a=2
      console.log(`修改后${test}`) // {a:1}
    
    2.writable

    我们可以看见,test对象已经有一个属性a,并且值为1.但是和上面一样,我们仍没有修改成功.接下来,我们使用 writable进行修改

      Object.defineProperty(test, "a",{
          value:1,
          writable:true
      })
      console.log(test); // {a:1}
      test.a=2
      console.log(`修改后${test}`) // {a:2}
    
    3.configurable

    我们可以看见,属性值已经被修改成功.接下来我们来看 configurable属性.

    Object.defineProperty(test, "a",{
          value:1,
          writable:true
    })
    delete test.a
    console.log(test) //{a:1}
    

    我们可以看见,此时我们无法删除掉属性a,我们修改代码,此时即可删除成功.

    Object.defineProperty(test, "a",{
          value:1,
          writable:true,
          configurable:true
    
    })
    delete test.a
    console.log(test) //{}
    

    我们上面说了configurable除下控制是否可以删除,还可以用来控制是否可以重新定义.我们来修改代码

    Object.defineProperty(test, "a",{
          value:1,
    })
    //重新定义
    Object.defineProperty(test, "a",{
          value:2,
    })
    

    发现报错,不能重新定义:

    image.png

    我们增加writable=true,即可重新定义

    Object.defineProperty(test, "a",{
          value:1,
          writable:true,
    })
    //重新定义
    Object.defineProperty(test, "a",{
          value:2,
    })
    console.log(test) //{a:2}
    

    或者设置configurable=true

     Object.defineProperty(test, "a",{
          value:1,
          writable:false, //也可以不写,默认false
          configurable:true
    })
    //重新定义
    Object.defineProperty(test, "a",{
          value:2,
    })
    console.log(test) //{a:2}
    
    4.enumerable

    1.Object.keys,返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性).

    Object.defineProperty(test, "a",{
          value:1,
    })
    console.log(Object.keys(test)); //[]
    

    2.for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)

     for(let i in test){
        console.log(i);
    } //不会输出,因为无法遍历,此时test相当于{}对象
    

    3.Object.values(),返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)的值

     console.log(Object.values(test)); //[]
    

    4.Object.entries(),返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)以及对应值组成一个数组

     console.log(Object.entries(test)); //[]
    

    5.Object.getOwnPropertyNames(),返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性).

    console.log(Object.getOwnPropertyNames(test)); //["a"]
    

    6.Reflect.ownKeys(),返回一个数组,包含对象自身的所有属性,不管属性名是Symbol或字符串,也不管是否可枚举.

    console.log(Reflect.ownKeys(test));["a"]
    

    我们可以看见,前3种无法获取对象的属性,我们设置enumerable=true,即可获取,代码就不贴了.

    5.get
    Object.defineProperty(test, "a",{
        // value:1, //get和value不能同时使用
        get(){            
            return 1
        }
    })
    console.log(test.a) //1
    console.log(test) //{}
    

    我们从打印结果可以看出

    • 当我们打印test.a时候,get函数会执行,此时a的值就是return的值;但我们直接打印test是不会执行get函数的.
    • 如果我们把value注释打开,会发现报错
    • get、set函数和value不能同时使用,都会报如下错误
    image.png
    6.set
    Object.defineProperty(test, "a",{
        get(){            
            return 1
        },
        set(newValue){
            console.log(newValue) //2
        }
    })
    console.log(test.a) //1
    test.a=2
    console.log(test.a) //1
    

    上面例子中,我们对属性a重新赋值为2,set函数可以接收到最新的值为2,但是get函数返回永远是1,所以test.a的值永远是1,我们更改下代码:

    let num=1
     Object.defineProperty(test, "a",{
        get(){            
            return num
        },
        set(newValue){
            console.log(newValue) //2
            num=newValue
        }
    })
    console.log(test.a) //1
    test.a=2
    console.log(test.a) //2
    

    3. 不同写法对比

    let test={};
    test.a=1
    等价于
    Object.defineProperty(test, "a",{
        value:1,
        configurable:true,
        writable:true,
        enumerable:true
    })
    

    Object.defineProperty(test, "a",{
        value:1,
    })
    等价于
     Object.defineProperty(test, "a",{
        value:1,
        configurable:false,
        writable:false,
        enumerable:false
    })
    

    4.拓展

    1.创建对象常量

    结合writable: false 和 configurable: false 就可以创建一个真正的常量属性(不可修改,不可重新定义或者删除),但可以添加新属性

    Object.defineProperty(test, "a",{
        value:1,
        configurable:false,
        writable:false
    })
    delete test.a;
    test.a=2
    console.log(test);  //{a:1}
    test.b=2
    console.log(test); //{a:1,b:2}
    Object.defineProperty(test, "a",{
        value:2,
    }) //报错 Cannot redefine property
    
    2.禁止扩展 Object.preventExtensions

    如果你想禁止一个对象添加新属性并且保留已有属性,就可以使用Object.preventExtensions()

    let test={a:1}
    Object.preventExtensions(test)
    test.b=2
    test.a=2
    console.log(test); //{a:2}
    Object.defineProperty(test, "a",{
        value:3,
    })
    console.log(test);  //{a:3}
    Object.defineProperty(test, "b",{
        value:2,
    }) //报错:Uncaught TypeError: Cannot define property b, object is not extensible
    

    我们可以发现,通过preventExtensions,我们无法再新增(拓展)属性,只能更改原来存在的属性.

    3.密封对象 Object.seal

    Object.seal()方法用于密封一个对象,这个方法实际上会在一个现有对象上调用object.preventExtensions()并把所有现有属性标记为configurable:false.即将对象设置为不可扩展,同时将对象的所有自有属性都设置为不可配置(包括Symbol值的属性)。也就是说,不能给对象添加新的属性和方法,也不能删除现有的属性和方法、不能修改现有属性和方法的配置。但如果对象的属性和方法是可写的,那该属性和方法仍然可以修改。

        let test={a:1}
        Object.seal(test)
        test.a=2
        test.b=3
        console.log(test); //{a:2}
        delete test.a
        console.log(test); //{a:2}
        Object.defineProperty(test, "a",{
                value:3,
        })
        console.log(test); //{a:3}
        Object.defineProperty(test, "b",{
                value:3,
        }) //报错:Uncaught TypeError: Cannot define property b, object is not extensible
    
    4.冻结对象 Object.freeze

    Object.freeze()会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal(),并把所有现有属性标记为writable: false,这样就无法修改它们的值。

    let test={a:1}
    Object.freeze(test)
    test.a=2
    test.b=3
    console.log(test); //{a:1}
    delete test.a
    console.log(test); //{a:1}
    Object.defineProperty(test, "b",{
            value:3,
    })
    console.log(test); //报错:Uncaught TypeError: Cannot define property b, object is not extensible
    

    相关文章

      网友评论

        本文标题:Object.defineProperty

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