Object.defineProperty() 是啥?
假设我们有个对象user ,我们要给他增加一个name属性 , 我们怎么做 ?
var user = {}; user.name = "若城" console.log(user)
如果要增加一个方法呢 ?
user.sayHi=function(){ console.log('hi') } console.log(user)
Object.defineProperty() 就是做这个事情的
如何使用Object.defineProperty()
Object.defineProperty 需要三个参数 (object , propName , descriptor)
- object 对象 ===》 给谁加
- propName 属性名 ===》 要加的属性的名字 【类型:Object】
- descriptor 属性描述 === 》 加的这个属性有什么样的特性【类型:Object】
目前我们知道了基本语法了 , 接下来我们来进行一系列的尝试吧 !!!!!
尝试一 :既然可以给一个对象增加属性 , 那么我们用它来给user 增加一个 name 属性
let user ={} Object.defineProperty(user, 'name' , { value:'vue 源码之 defineProperty' }) console.log(user)
打印结果
说明那个经典的value 属性依旧是设置属性值的
思考 : 属性值只能是字符串吗? 可不可以是别的属性呢 ?如:number 、 function 、 Object 、 boolean …
尝试二 : 属性值是否可以是除字符串以外的类型
Object.defineProperty(user, 'name',{ // str value:'若城' }) Object.defineProperty(user,'isShow',{ // 布尔值 value:true }) Object.defineProperty(user,'handleEat',{ // 函数 value:function(){ console.log('麻辣虾尾') } }) Object.defineProperty(user,'age',{ // num value:18 }) Object.defineProperty(user,'default',{ // 对象 value:{ hobby:'PLAY', birth:'2020-1-1', drink:'肥宅快乐水' } }) console.log(user)
打印结果
事实证明任何数据类型的数据都是可以的
尝试三 : 如果user对象已经有了 name 属性 , 我们还可以通过 Object.defineProperty 来改变这个值吗?
Object.defineProperty(user,'name',{ value:'若城' }) user.name="新-若城" console.log(user)
我们从打印结果发现,name的值并没有被改变, 为啥嘞??? 我们进行深入的剖析
经查相关资料我们发现, 关于 Object.defineProperty() 他的第三个参数 descriptor 是有很多个参数的 除了 value 属性还有以下属性
- writable : 属性的值是否可以被重写。设置为true可以被重写;设置为false,不能被重写。默认为false。
- enumerable: 此属性是否可以被枚举(使用for…in或Object.keys())。设置为true可以被枚举;设置为false,不能被枚举。默认为false。
- configurable:是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)。设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false。这个属性起到两个作用:1.目标属性是否可以使用delete删除 2.目标属性是否可以再次设置特性
提示:一旦使用Object.defineProperty给对象添加属性,那么如果不设置属性的特性,那么configurable、enumerable、writable这些值都为默认的false
接下来我们进行一次实践:
Object.defineProperty(user,'name',{ value:'若城', writable:true }) user.name="新-若城" console.log(user)
完美解决
尝试四:enumerable 【难度升级】
enumerable: 此属性是否可以被枚举(使用for…in或Object.keys())。设置为true可以被枚举;设置为false,不能被枚举。默认为false。
看完enumerable 的解释是不是迷迷糊糊的 , 突然感觉到 3 * 5 = 35 这个 3 * 5 也太难了 ,我们一点点的来解决下
第一点 :假设我们想知道user对象有哪些属性 , 我们一般会这样去做
let user={ name:'若城', age:18, hobby:'睡觉' } // es6 let keys=Object.keys(user) console.log(keys) // ["name", "age", "hobby"] // es5 let keys = [] for (const key in user) { keys.push(key) } console.log(keys)// ["name", "age", "hobby"]
如果我们使用 Object.的方式定义属性会发生什么呢?
let user={ name:'若城', age:18, hobby:'睡觉' } // 定义一个性别, 可以被枚举 Object.defineProperty(user,'gender',{ value:'男', enumerable:true }) // 定义一个对象 , 不可以被枚举 Object.defineProperty(user,'default',{ value:{ hobby:'PLAY', birth:'2020-1-1', drink:'肥宅快乐水' }, enumerable:false }) // es6 let keys= Object.keys(user) console.log(keys) //["name", "age", "hobby", "gender"] // es5 let keys = [] for (const key in user) { keys.push(key) } console.log(keys) //["name", "age", "hobby", "gender"]
**通过结果我们发现, enumerable属性值为true 时可以被枚举, 不为true时是不可以被枚举的 **
尝试五: configurable 这个属性有两个作用 1. 属性是否可以被删除 2.属性的特性在第一次设置之后可否被重新定义特性
let user={ name:'若城', age:18 } // 定义一个性别,不可以被删除和重新定义特性 Object.defineProperty(user, 'gender',{ value:'男', enumerable:true, configurable:false }) // 尝试删除 delete user.gender console.log(user) //{name: "若城", age: 18, gender: "男"} // 重新定义特性 Object.defineProperty(user,'gender',{ value:'男', enumerable:true, configurable:true }) // 报错如下: // Uncaught TypeError: Cannot redefine property: gender // at Function.defineProperty (<anonymous>)
修改为 可以被删除和重新定义特性试一下
let user={ name:'若城', age:18 } // 定义一个性别,可以被删除和重新定义特性 Object.defineProperty(user, 'gender',{ value:'男', enumerable:true, configurable:true }) // 尝试删除 delete user.gender console.log(user) //{name: "若城", age: 18} // 重新定义特性 Object.defineProperty(user,'gender',{ value:'男', enumerable:true, configurable:false }) // 删除前 console.log(user) //{name: "若城", age: 18, gender: "男"} // 删除一下 delete user.gender console.log(user) //{name: "若城", age: 18, gender: "男"}
configurable设置为 true 则该属性可以被删除和重新定义特性;反之属性是不可以被删除和重新定义特性的,默认值为false
尝试六:set 和 get (即 存取器描述: 定义属性如何被存取)
代码走起
let user = { name:'若城' } let count =12 // 定义一个age 获取值时返回定义好的变量count Object.defineProperty(user , 'age',{ get :function(){ return count } }) console.log(user.age)
通过代码我们发现 ,在get到该属性的时候我们可以自由发挥值得操作 (比如return 一个表达式 等等 )
接下来我们看下 set
let user= { name:'若城' } var count = 12 // 定义一个age 获取值时返回定义好的变量Count Object.defineProperty(user,'age',{ get:function(){ return count }, set: function(newVal){ count = newVal } }) console.log(user.age) //12 user.age = 123 console.log(user.age) //123 console.log(count) //123
注意
当使用了getter或setter方法,不允许使用writable和value这两个属性(如果使用,会直接报错滴)
get 是获取值的时候的方法,类型为 function ,获取值的时候会被调用,不设置时为 undefined
set 是设置值的时候的方法,类型为 function ,设置值的时候会被调用,undefined
get或set不是必须成对出现,任写其一就可以
写在最后
Object.defineProperty方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象
- value: 设置属性的值
- writable: 值是否可以重写。true | false
- enumerable: 目标属性是否可以被枚举。true | false
- configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false
- set: 目标属性设置值的方法
- get:目标属性获取值的方法
下期预告 vue3 的 proxy 属性解锁