Object.defineProperty()

引言

  • 今天就来了解一下Object.defineProperty方法,直到今天才发现原来有这么一个神奇的方法

描述

该方法允许精确添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in 或 Object.keys 方法), 这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改的。

参数 Object.defineProperty(obj, prop, descriptor)

  • obj: 要在其上定义属性的对象。
  • prop: 要定义或修改的属性的名称。
  • descriptor: 将被定义或修改的属性描述符。
  • 返回值: 被传递给函数的对象。

属性描述符

  • 分为数据描述符存储描述符

configurable、enumerable数据、存储描述符共有,而value、writableget、set只能拥有其中之一

说明

  • configurable
    • 当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。
  • enumerable
    • 当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中(才能被例如for...in和Object.keys()遍历)。
  • value
    • 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。
  • writable
    • 当且仅当该属性的writable为true时,value才能被修改。
  • get
    • 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
  • set
    • 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
configurable enumerable value writable get set
数据描述符 Yes Yes Yes Yes No No
存取描述符 Yes Yes No No Yes Yes
默认值 false false undefined false undefined undefined

数据描述符示例

 let data = {
    name: '车神-黄杰',
    age: 23
 }
 // 为data对象添加一个 gender属性 并把 writable设置为:true 该值可被修改
 Object.defineProperty(data, 'gender', {
    value : '男',
    writable : true,
    enumerable : true,
    configurable : true
 })

 // 为data对象添加一个 like属性 并把 writable设置为:false 该值不可被修改,即使修改不会报错,但无效果
 Object.defineProperty(data, 'like', {
    value : '敲代码',
    writable : false,
    enumerable : true, 
    configurable : true
 })

 console.log('修改前')
 console.log(data)

 data.like = '还喜欢爬山'
 data.gender = '女'

 console.log('修改后')
 console.log(data)

打印结果

  • 可看到like属性无法修改,其他配置可自行测试

存取描述符示例

  • get

该方法在获取某对象的属性时调用,并且该属性值由该方法返回值决定,没有参数

  • set

该方法在设置某对象的属性值时调用,参数即为设置的属性值

 let data = {}

 let obj = {
    name: '车神-黄杰',
    age: 23
 }

 // name属性
 Object.defineProperty(data, 'name', {
    get(){
        return obj.name
    },
    set(val){
        obj.name = val
    },
    enumerable : true, 
    configurable : true 
 })

 // age属性
 Object.defineProperty(data, 'age', {
    get(){
        // 这里 不返回任何值 即默认返回 undefined
        // return obj.age
    },
    set(val){
        obj.age = val
    },
    enumerable : true, 
    configurable : true 
 })

 data.name = '我爱前端'
 data.age = 53

 // 获取name数据
 console.log('data.name: '+ data.name)
 console.log('obj.name: '+ obj.name)
 // 获取age数据
 console.log('data.age: '+ data.age)
 console.log('obj.age: '+ obj.age)

打印结果

  • 修改了data.name属性,会调用set方法,即会执行obj.name = val,因此obj.name设置为我爱前端,当获取data.name属性值时,会调用get方法,值即为该方法的返回值,即obj.name,因此data.name也为我爱前端

  • 同理obj.age = 53也无需在解释,但是为什么data.ageundefined,我们可以看到已经把return obj.age注释了,因此它的get方法返回值为undefined

扩展

  • 如果能明白Object.defineProperty()的基本用法,就不难明白Vue组件data为什么不是对象,而是一个函数了,官方文档说明Vue实例其实是代理了对data对象属性的访问,正如存取描述符示例的代码,data其实也是代理了对obj对象属性的访问,

  • 看下面的代码,data1、data2同时都代理了对obj对象属性访问,当修改data2.name值为che_hj,则data1.name也设置为che_hj,这很明显就是不是我们想要的,修改data2.name不应该影响data1.name的值,因此obj应该为一个函数,这样就行了吗?想多了,还需要再敲多两行代码才能实现我们想要的效果,不过这里就不再累赘了。

 let obj = {
    name: '车神-黄杰',
    age: 23
 }

 let data1 = {}
 let data2 = {}

 // 对象data1
 Object.defineProperty(data1, 'name', {
    get(){
        return obj.name
    },
    set(val){
        obj.name = val
    },
    enumerable : true, 
    configurable : true 
 })

 // 对象 data2
 Object.defineProperty(data2, 'name', {
    get(){
        return obj.name
    },
    set(val){
        obj.name = val
    },
    enumerable : true, 
    configurable : true 
 })

 // 值修改为 che_hj
 data2.name = 'che_hj'

 console.log('obj.name: '+ obj.name)
 console.log('data1.name: '+ data1.name)
 console.log('data2.name: '+ data2.name)

打印结果

  • 可以看到 data1.name也设置为了che_hj
原文地址:https://www.cnblogs.com/HJ412/p/11909660.html