Object相关操作(一),defineProperty、defineProperties、getOwnPropertyDescriptor等

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 #
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来实现数据双向绑定




    
    Title
    


随机变化

你可能感兴趣的:(Object相关操作(一),defineProperty、defineProperties、getOwnPropertyDescriptor等)