美文网首页
Object相关操作(一),defineProperty、def

Object相关操作(一),defineProperty、def

作者: 石豌豆 | 来源:发表于2019-11-16 14:50 被阅读0次

    1、defineProperty和defineProperties

    字面意义就表明一个是操作单个,一个是操作多个。
    defineProperty的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性

    Object.defineProperty(obj, prop, desc),defineProperty接收参数:
    obj: 需要定义属性的当前对象
    prop: 当前需要定义的属性名
    desc: 属性描述符

    let obj = {}
    Object.defineProperty(obj, 'name', {
        value: 2, // 值
        configurable: true, // 是否可配置、可删除
        writable: true, // 是否可修改
        enumerable: true // 是否可枚举、遍历
    })
    console.log(obj) // {name: 2}
    Object.defineProperty(obj, 'name', {
        value: 3,
        configurable: true,
        writable: true,
        enumerable: true
    })
    console.log(obj) // {name: 3}
    

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

    Object.defineProperties(obj, props), 接收参数:
    obj: 需要定义属性的当前对象(同defineProperty)
    props: 对象,包含需要定义的属性及其描述

    let obj = {}
    Object.defineProperties(obj, {
        'name': {
            value: 'lily', // 值
            configurable: true, // 是否可配置、可删除
            writable: true, // 是否可修改
            enumerable: true // 是否可枚举、遍历
        },
        'age': {
            value: 20, // 值
            configurable: true, // 是否可配置、可删除
            writable: true, // 是否可修改
            enumerable: true // 是否可枚举、遍历
        }
    })
    console.log(obj) // {name: 'lily', age: 20}
    

    大家会看到这里每次设置属性描述都包含很多参数,这里列举一下desc中每个参数的意义

    • value:
      对应属性的属性值
      默认 undefined
      只要 writable 和 configurable 有一个为 true,就允许改动

    • get:
      一个函数,表示该属性的取值函数, 不接收参数, return 一个值
      默认 undefined

    • set:
      一个函数,表示该属性的存值函数, 接受一个参数
      默认 undefined

    • configurable:
      是否可配置、可删除, 布尔值
      true表示该属性可通过defineProperty配置,也可删除
      false则相反

    • writable:
      是否可写,如obj.a = 3这样赋值, 布尔值
      如果为 true , 则obj.a=3生效,反之赋值失败

    • enumerable:
      是否可枚举、遍历,布尔值
      如果为false, 比如for in、Object.keys()获取不到该值。

    下面举几个例子看看对应的情况
    1、描述默认值

    1、普通赋值对象
    let obj = {}
    obj.a = 3
    相当于
    let obj = {}
    Object.defineProperty(obj, 'a', {
        value: 3, // 值
        configurable: true, // 这里默认为true
        writable: true, // 这里默认为true
        enumerable: true // 这里默认为true
    })
    
    2、使用defineProperty定义
    Object.defineProperty(obj, 'a', {
        value: 3
    })
    相当于
    Object.defineProperty(obj, 'a', {
        value: 3, // 值
        configurable: false,
        writable: false,
        enumerable: false
    })
    
    两种赋值方式所对应的描述默认值是不同的
    

    2、存取描述get/set

    let obj = {}
    let aaa
    Object.defineProperty(obj, 'a', {
        get: function () {
            console.log('getter')
            return aaa
        },
        set: function (val) {
            console.log('setter', val)
            aaa = val
        }
    })
    console.log(obj.a) // getter -> undefined
    obj.a = 333 // setter 333
    console.log(obj.a) // getter -> 333
    

    3、configurable、writable单个和配合场景

    'use strict'
    let obj = {}
    Object.defineProperty(obj, 'a', {
        value: 2,
        configurable: false,
        writable: true,
        enumerable: true
    })
    delete obj.a // 报错 Uncaught TypeError: Cannot delete property 'a' of #<Object>
    obj.a = 333
    console.log(obj.a) // configurable为false,writable为true时赋值成功
    Object.defineProperty(obj, 'a', {
        value: 444
    })
    console.log(obj.a) // 444 这个时候是配置成功的,为什么呢?
    ---------------------------------------------
    我们把之前的配置中只配置configurable试试
    Object.defineProperty(obj, 'a', {
        value: 2,
        configurable: false
    })
    Object.defineProperty(obj, 'a', {
        value: 444
    }) // 只配置configurable时报错 Uncaught TypeError: Cannot redefine property: a
    

    通过上面得出configurable和writable配合的使用场景

    • configurable=false,writable=false
      不可配置、删除,不可赋值。
    • configurable=true,writable=true
      可配置、可删除,可赋值。
    • configurable=false,writable=true
      obj.a = 333和Object.defineProperty(obj, 'a', {
      value: 444
      })这两个都是生效的

    但有个特殊情况需要注意,再次配置时可以把writable由true变成false,但不能由false变为true。

    • configurable=true,writable=false
      obj.a = 333这样赋值会报错
      Object.defineProperty(obj, 'a', {
      value: 444
      })这样配置是生效的。

    4、enumerable枚举配置

    'use strict'
    let obj = {}
    Object.defineProperty(obj, 'a', {
        value: 2,
        configurable: true,
        writable: true,
        enumerable: false
    })
    obj.b = 3
    console.log(Object.keys(obj)) // ['b']
    console.log(obj.propertyIsEnumerable('a')) // false
    console.log(obj.propertyIsEnumerable('b')) // true
    

    2、getOwnPropertyDescriptor

    获取指定对象的自身属性描述符。自身属性描述符是指直接在对象上定义(而非从对象的原型继承)的描述符。

    getOwnPropertyDescriptor(obj, keyname)接收两个参数
    obj: 必传,包含属性的对象
    keyname: 必传,指定的属性名

    'use strict'
    let obj = {}
    Object.defineProperty(obj, 'a', {
        value: 2,
        configurable: true,
        writable: true,
        enumerable: false
    })
    console.log(Object.getOwnPropertyDescriptor(obj, 'a')) // {value: 2, writable: true, enumerable: false, configurable: true}
    
    这个方法对应也有获取所有属性的描述,返回一个对象,包含所有属性及描述
    console.log(Object.getOwnPropertyDescriptors(obj)) // {a: {…}}
    

    这里我们顺便回顾下默认值

    'use strict'
    let obj = {}
    Object.defineProperty(obj, 'a', {
        value: 2,
        configurable: true,
        writable: true,
        enumerable: false
    })
    obj.b = 3
    Object.defineProperty(obj, 'c', {
        value: 4
    })
    console.log(Object.getOwnPropertyDescriptors(obj))
    
    image.png

    上面针对配置方法和获取配置方法做了一个简单的描述,那这些对我们来说有什么用或者有什么扩展呢?

    1、给对象属性添加常量,不可修改,达到const a = 1这种效果。

    这里我们就可以使用configurable和writable来实现
    'use strict'
    let obj = {}
    Object.defineProperty(obj, 'a', {
        value: 1,
        configurable: false,
        writable: false
    })
    obj.a = 2 // 报错
    Object.defineProperty(obj, 'a', {
        value: 2
    }) // 报错
    delete obj.a // 不可删除
    

    2、禁止扩展
    这里会使用到一个方法,Object.preventExtensions()

    'use strict'
    let obj = {a: 1}
    Object.preventExtensions(obj) // 禁止扩展
    obj.a = 2
    console.log(obj.a) // 2
    obj.b = 3 // Uncaught TypeError: Cannot add property b, object is not extensible
    console.log(obj.b)
    Object.defineProperty(obj, 'b', {
        value: 3
    }) // Cannot define property b, object is not extensible
    console.log(obj.b)
    

    使用了禁止扩展,修改原属性是可以的,但添加新属性都会报错

    3、密封
    这里也是一个新方法,Object.seal()会创建一个密封的对象,这个方法实际上会在一个现有对象上调用object.preventExtensions(...)并把所有现有属性标记为configurable:false。

    'use strict'
    let obj = {a: 1}
    Object.seal(obj) // 密封
    obj.a = 2
    console.log(obj.a) // 2
    Object.defineProperty(obj, 'a', {
        value: 3
    })
    console.log(obj.a) // 3
    Object.defineProperty(obj, 'a', {
        value: 4,
        configurable: true
    }) // 报错
    console.log(obj.a)
    可赋值,不可修改配置
    
    'use strict'
    let obj = {a: 1}
    Object.seal(obj) // 密封
    obj.b = 3 // Uncaught TypeError: Cannot add property b, object is not extensible
    console.log(obj.b)
    Object.defineProperty(obj, 'b', {
        value: 3
    }) // Cannot define property b, object is not extensible
    console.log(obj.b)
    不可扩展
    

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

    'use strict'
    let obj = {a: 1}
    Object.freeze(obj) // 冻结
    obj.a = 2 // 报错
    console.log(obj.a) 
    Object.defineProperty(obj, 'a', {
        value: 3
    }) // 报错
    console.log(obj.a) 
    Object.defineProperty(obj, 'a', {
        value: 4,
        configurable: true
    }) // 报错
    console.log(obj.a)
    

    三者对比:
    禁止扩展: 可修改原属性的值、可配置原属性、不可添加新属性
    密封:可修改原属性值、不可配置原属性、不可添加新属性
    冻结:不可修改原属性值、不可配置原属性、不可添加新属性

    最后来尝试用defineProperty来实现数据双向绑定

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script>
            let data = {}
            Object.defineProperty(data, 'val', {
                get: function () {
                    return data.val
                },
                set: function (val) {
                    document.getElementsByClassName('input')[0].value = val
                    document.getElementsByClassName('showBox')[0].innerHTML = val
                }
            })
    
            function change() {
                data.val = (Math.random(0, 100) * 100).toFixed(0)
            }
    
            function inputChange(e) {
                data.val = e.target.value
            }
        </script>
    </head>
    <body>
    <div>
        <input class="input" type="text" oninput="inputChange(event)">
        <div class="showBox"></div>
        <div onClick="change()">随机变化</div>
    </div>
    </body>
    </html>
    

    相关文章

      网友评论

          本文标题:Object相关操作(一),defineProperty、def

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