Object.definePerproty
对象的定义与赋值
经常使用的定义与赋值方法
obj.key =value
或者
obj['key']=value
第二种的key可以是变量
代码落地
let obj = {}
// 通过obj.key = value 给对象添加属性
obj.name = 'zs'
/**
* 通过obj[key]给对象添加属性
* 1. 定义变量
* 2. obj[year变量] = 18
*/
year = 'age'
obj[year] = 18
// 打印obj
// { name: 'zs', age: 18 }
console.log(obj)
Object.defineProperty()语法说明
Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
/**
* obj 是需要定义的对象
* key 是需要定义的属性名
* desc 属性描述符
*/
Object.defineProperty(obj, key, desc)
一般通过为对象的属性赋值的情况下,对象的属性可以修改也可以删除,但是通过Object.defineProperty()定义属性,通过描述符的设置可以进行更精准的控制对象属性。
属性的特性以及内部属性
javacript 有三种类型的属性
- 命名数据属性:拥有一个确定的值的属性。这也是最常见的属性
- 命名访问器属性:通过getter和setter进行读取和赋值的属性
- 内部属性:由JavaScript引擎内部使用的属性,不能通过JavaScript代码直接访问到,不过可以通过一些方法间接的读取和设置。比如,每个对象都有一个内部属性[[Prototype]],你不能直接访问这个属性,但可以通过Object.getPrototypeOf()方法间接的读取到它的值。虽然内部属性通常用一个双吕括号包围的名称来表示,但实际上这并不是它们的名字,它们是一种抽象操作,是不可见的,根本没有上面两种属性有的那种字符串类型的属性
属性描述符
通过Object.defineProperty()为对象定义属性,有两种形式,且不能混合使用,分别为数据描述符,存取描述符,下面分别描述下两者的区别:
数据描述符 –特有的两个属性(value,writable)
let obj = {}
/**
* value是 对象的值 key的形式在Object.defineProperty的第二个参数 'name'
* writable: 是否可以修改 默认false
*/
Object.defineProperty(obj, 'name', {
value: '黑手',
writable: false // 是否可以修改
})
obj.name = '神鹰'
/**
* 当 writable 1. 为 true 时可以修改属性 这时候的log是神鹰 因为通过了obj.name修改了name属性
* 2. 为 false 时不可以修改属性 这个时候log是黑手 即使外部修改了但是值也没有变
*/
console.log(obj.name)
注意,如果描述符中的某些属性被省略,会使用以下默认规则:
/**
* 属性名 默认值
* value undefined
* get undefined
* set undefined
* writable false
* enumberable false
* configurable false
*/
存取描述符
–是由一对 getter、setter 函数功能来描述的属性
- get:一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined。
- set:一个给属性提供setter的方法,如果没有setter则为undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined。
let obj = {}
let temp = null
Object.defineProperty(obj, 'name', {
get() {
return 123
},
set(val) {
temp = val
}
})
/**
********** 只触发了get方法 ***********
* 1. 获取.name属性的时候会触发get方法 之后会return出123
* 2. 这个时候set并没有触发所以temp的值是null
* 3. log出的结果 obj.name是 123 temp的值为 null
*/
obj.name = '黑手'
/**
* ******* 增加上行代码 会触发 *********
* 1. 修改了name属性的时候会触发 set 和 get方法
* 2. 这个时候get return 123 此时的obj.name = 123
* 3. 触发set方法 tmep = val 此时的temp为 黑手
* 此时的log是 obj.name是 123 temp的值为 黑手
*/
console.log('obj.name是',obj.name,'temp的值为',temp)
数据描述符和存取描述均具有以下描述符
- configrable 描述属性是否配置,以及可否删除 ()
// 必须开启严格模式
'use strict'
let obj = {}
Object.defineProperty(obj, 'name', {
value: '黑手',
configurable: true,
})
/**
* configurable 可否删除
* 1. 默认false 不可以删除
* 当我们执行 delete 时会报错 **TypeError: Cannot delete property 'name' of #<Object>**
* 2. 为 true时 可以删除
* 这个时候log出的.name 就是 undefined
*/
delete obj.name
console.log(obj.name)
出现问题
解决办法
- configurable: false 时,不能删除当前属性,且不能重新配置当前属性的描述符(有一个小小的意外:可以把writable的状态由true改为false,但是无法由false改为true),但是在writable: true的情况下,可以改变value的值
- configurable: true时,可以删除当前属性,可以配置当前属性所有描述符。
- enumerable 描述属性是否会出现在for in 或者 Object.keys()的遍历中
let obj = {}
Object.defineProperty(obj,'name', {
value: '黑手',
enumerable: false
})
obj.age = 18
Object.defineProperty(obj, 'sex', {
value:'男',
enumerable: true
})
/**
* enumberable: fasle 描述是 属性是否会出现在for in 或者是 Object.keys
* 默认值 false 不会出现在遍历的对象上
* true 会出现在遍历节点上
* 观察下方打印 [ 'age', 'sex' ] 验收以上效果
*
*/
console.log(Object.keys(obj))
注意:以下二种区别
(1) : 区别1
let obj = {}
obj.name = '黑手'
// 等价于 ↓
Object.defineProperty(obj, 'name', {
value: '黑手',
configurable: true,
enumerable: true,
writable: true
})
/**
* obj.name 添加的属性名
* 1. 出现在for in 等价 → enumerable: true
* 2. obj.name是可修改的 等价 → writable: true
* 3. obj.name是可删除的 等价 → configurable: true
*/
console.log(obj.name)
(2) : 区别2
let obj ={}
Object.defineProperty(obj, 'name', {
value: '黑手'
})
// 等价于 ↓
Object.defineProperty(obj,'name', {
value: '黑手',
configurable: false,
enumerable: false,
writable: false
})
/**
* obj.name 添加的属性名
* 1. 不会出现在for in 等价 → enumerable: false
* 2. obj.name是不可修改的 等价 → writable: false
* 3. obj.name是不可删除的 等价 → configurable: false
*/
console.log(obj.name)
不变性
- 对象常量
结合writable: false 和 configurable: false 就可以创建一个真正的常量属性(不可修改,不可重新定义或者删除)
// 必须开启严格模式
"use strict"
let obj = {}
Object.defineProperty(obj, "name", {
value: '黑手',
configurable: false, // 不可删除
writable: false // 不可修改
})
/**
* 通过以两个描述符 我们就创建出来一个 包含了一下 **所有特征** 的对象
* 1. 不可删除 当你删除的时候 会抛出错误 Cannot delete property 'name' of #<Object>
* 2. 不可修改的对象 当你修改时候 会抛出错误 Cannot assign to read only property 'name' of object '#<Object>'
* 3. 不可重新定义 当你重新定义 会抛出错误 Cannot redefine property: name
*/
Object.defineProperty(obj, 'name', {
value: '新黑手' // Cannot redefine property: name
})
delete obj.name // Cannot delete property 'name' of #<Object>
obj.name = '123' // Cannot assign to read only property 'name' of object '#<Object>'
console.log(obj.name)
- 禁止扩展
如果你想禁止一个对象添加新属性并且保留已有属性,就可以使用
Object.preventExtensions(…)
'use strict'
let obj = {
age: 18
}
Object.preventExtensions(obj)
obj.name = 'zs'
/**
* 不可扩展 Object.preventExtensions(对象名)
* 特性: 保留自己 已有属性 不可添加新的属性
* 开启严格模式会报错 Cannot add property name, object is not extensible
* 不开启就是undefined
*/
console.log(obj.name)
let obj = {
age: 18
}
Object.preventExtensions(obj)
Object.defineProperty(obj, 'age', {
value: 100,
writable: true
})
/**
* 上述代码说明可以进行配置
* log出来的age是100
*/
console.log(obj.age)
- 密封
Object.seal()
会创建一个密封的对象,这个方法实际上会在一个现有对象上调用
密封之后不可以通过.key = value添加,也不可以通过Object.defineProperty配置
'use strict'
let obj = {}
Object.seal(obj)
/**
* 密封之后不可以通过
* .name添加属性 抛出错误 Cannot add property name, object is not extensible
* 也不可以通过defineProperty() 配置 抛出错误 Cannot define property age, object is not extensible
*/
obj.name = 'zs' // Cannot add property name, object is not extensible
Object.defineProperty(obj, 'age', {
value: 18
})
console.log(obj.age) // Cannot define property age, object is not extensible
-
密封
Object.freeze()会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal(),并把所有现有属性标记为writable: false,这样就无法修改它们的值。
'use strict'
let obj = {
name: '黑手'
}
Object.freeze(obj)
// obj.age = 18
obj.name = 'zs' // Cannot assign to read only property 'name' of object '#<Object>'
console.log(obj.age) // Cannot add property age, object is not extensible
这个方法是你可以应用在对象上级别最高的不可变性,它会禁止对于对象本身及其任意直接属性的修改(但是这个对象引用的其他对象是不受影响的)
你可以深度冻结一个对象,具体方法为,首先这个对象上调用Object.freeze()然后遍历它引用的所有对象,并在这些对象上调用Object.freeze()。但是一定要小心,因为这么做有可能会无意中冻结其他共享对象。
代码地址:
https://github.com/zhzweb634/Object.defineProperty