Object.definePerproty

  • Post author:
  • Post category:其他




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 有三种类型的属性

  1. 命名数据属性:拥有一个确定的值的属性。这也是最常见的属性
  2. 命名访问器属性:通过getter和setter进行读取和赋值的属性
  3. 内部属性:由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 函数功能来描述的属性

  1. get:一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined。
  2. 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)


数据描述符和存取描述均具有以下描述符

  1. 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)


出现问题


在这里插入图片描述

在这里插入图片描述


解决办法


在这里插入图片描述

  1. configurable: false 时,不能删除当前属性,且不能重新配置当前属性的描述符(有一个小小的意外:可以把writable的状态由true改为false,但是无法由false改为true),但是在writable: true的情况下,可以改变value的值
  2. configurable: true时,可以删除当前属性,可以配置当前属性所有描述符。
  1. 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)


不变性

  1. 对象常量

结合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)

  1. 禁止扩展

如果你想禁止一个对象添加新属性并且保留已有属性,就可以使用


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)
  1. 密封


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
  1. 密封

    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



版权声明:本文为m0_49304996原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。