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 #<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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351