Object.defineProperty

在了解什么是Object.defineProperty前,我们先回忆下我们平常经常使用的 对象

1.对象的赋值

我们平常一般使用obj.prop=value或者obj['prop']=value对对象进行赋值或修改,如

let test={}
test.a=1
test["b"]=2
console.log(test.a) //1
console.log(test.b) //2

那么Object.defineProperty又和上面的对象赋值是什么关系呢?从字面(defineProperty)意思上就可以知道--定义属性,所以Object.defineProperty也是一种对对象属性修改或赋值的方式,只不过我们可以进行更精确的控制.

2.Object.defineProperty的定义:

Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,语法如下,有三个参数:

Object.defineProperty(obj, prop, descriptor)
  • obj:需要定义或修改属性的对象(必填)
  • prop:需要定义的属性名称(必填)
  • descriptor:要定义或修改的属性描述符,是一个对象.(必填)

看到这,可能有的人不理解什么是属性描述符号?我们上面说的 对对象属性进行更精确的控制,就是通过descriptor来操控的.descriptor是一个对象,具有以下属性:

属性名 描述 默认值
value 要定义的属性对应的值 undefined
writable 要定义的属性值是否可以被改变 false
configurable 要定义的属性是否配置(配置指的是重新定义),以及可否删除 false
enumerable 要定义的属性是否会出现在对象的枚举属性中,如for in 或者 Object.keys()的遍历中 false
get 要定义的属性的getter函数,当访问该属性时,会执行此函数 undefined
set 要定义的属性的setter函数,当属性值被修改时,会调用此函数,此时该方法接受一个参数(也就是修改的新值) undefined

注意:这里说的要定义的属性指的是通过Object.defineProperty传递prop参数进行定义的属性(才可以控制是否删除、改变、监听等),如果对象本来就存在一些属性,但没有经过Object.defineProperty进行重新定义,是不受控制的,和我们最开始讲到的我们平常使用对象的方式,可以随便改变、删除,不能监听
下面我们将分别对这些属性描述符进行举例说明:

1.value

我们要在对象上定义属性,obj、prop必填就不用说了,descriptor也是必填的,否则会报错

let test={}
Object.defineProperty(test, "a")
console.log(test);
image.png

从报错信息我们可知,属性描述符必须是个对象,所以我们修改下就可以了

Object.defineProperty(test, "a",{})
console.log(test); // {a:undefined}
test.a=1
console.log(`修改后${test}`) // {a:undefined}

此时我们已经使用Object.defineProperty成功定义了一个属性,只不过没有值罢了.
但我们修改,打印出来发现值仍为undefined.这个后面会说,接下来我们先进行使用 value进行赋值操作.

  Object.defineProperty(test, "a",{
      value:1
  })
  console.log(test); // {a:1}
  test.a=2
  console.log(`修改后${test}`) // {a:1}
2.writable

我们可以看见,test对象已经有一个属性a,并且值为1.但是和上面一样,我们仍没有修改成功.接下来,我们使用 writable进行修改

  Object.defineProperty(test, "a",{
      value:1,
      writable:true
  })
  console.log(test); // {a:1}
  test.a=2
  console.log(`修改后${test}`) // {a:2}
3.configurable

我们可以看见,属性值已经被修改成功.接下来我们来看 configurable属性.

Object.defineProperty(test, "a",{
      value:1,
      writable:true
})
delete test.a
console.log(test) //{a:1}

我们可以看见,此时我们无法删除掉属性a,我们修改代码,此时即可删除成功.

Object.defineProperty(test, "a",{
      value:1,
      writable:true,
      configurable:true

})
delete test.a
console.log(test) //{}

我们上面说了configurable除下控制是否可以删除,还可以用来控制是否可以重新定义.我们来修改代码

Object.defineProperty(test, "a",{
      value:1,
})
//重新定义
Object.defineProperty(test, "a",{
      value:2,
})

发现报错,不能重新定义:

image.png

我们增加writable=true,即可重新定义

Object.defineProperty(test, "a",{
      value:1,
      writable:true,
})
//重新定义
Object.defineProperty(test, "a",{
      value:2,
})
console.log(test) //{a:2}

或者设置configurable=true

 Object.defineProperty(test, "a",{
      value:1,
      writable:false, //也可以不写,默认false
      configurable:true
})
//重新定义
Object.defineProperty(test, "a",{
      value:2,
})
console.log(test) //{a:2}
4.enumerable

1.Object.keys,返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性).

Object.defineProperty(test, "a",{
      value:1,
})
console.log(Object.keys(test)); //[]

2.for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)

 for(let i in test){
    console.log(i);
} //不会输出,因为无法遍历,此时test相当于{}对象

3.Object.values(),返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)的值

 console.log(Object.values(test)); //[]

4.Object.entries(),返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)以及对应值组成一个数组

 console.log(Object.entries(test)); //[]

5.Object.getOwnPropertyNames(),返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性).

console.log(Object.getOwnPropertyNames(test)); //["a"]

6.Reflect.ownKeys(),返回一个数组,包含对象自身的所有属性,不管属性名是Symbol或字符串,也不管是否可枚举.

console.log(Reflect.ownKeys(test));["a"]

我们可以看见,前3种无法获取对象的属性,我们设置enumerable=true,即可获取,代码就不贴了.

5.get
Object.defineProperty(test, "a",{
    // value:1, //get和value不能同时使用
    get(){            
        return 1
    }
})
console.log(test.a) //1
console.log(test) //{}

我们从打印结果可以看出

  • 当我们打印test.a时候,get函数会执行,此时a的值就是return的值;但我们直接打印test是不会执行get函数的.
  • 如果我们把value注释打开,会发现报错
  • get、set函数和value不能同时使用,都会报如下错误
image.png
6.set
Object.defineProperty(test, "a",{
    get(){            
        return 1
    },
    set(newValue){
        console.log(newValue) //2
    }
})
console.log(test.a) //1
test.a=2
console.log(test.a) //1

上面例子中,我们对属性a重新赋值为2,set函数可以接收到最新的值为2,但是get函数返回永远是1,所以test.a的值永远是1,我们更改下代码:

let num=1
 Object.defineProperty(test, "a",{
    get(){            
        return num
    },
    set(newValue){
        console.log(newValue) //2
        num=newValue
    }
})
console.log(test.a) //1
test.a=2
console.log(test.a) //2

3. 不同写法对比

let test={};
test.a=1
等价于
Object.defineProperty(test, "a",{
    value:1,
    configurable:true,
    writable:true,
    enumerable:true
})

Object.defineProperty(test, "a",{
    value:1,
})
等价于
 Object.defineProperty(test, "a",{
    value:1,
    configurable:false,
    writable:false,
    enumerable:false
})

4.拓展

1.创建对象常量

结合writable: false 和 configurable: false 就可以创建一个真正的常量属性(不可修改,不可重新定义或者删除),但可以添加新属性

Object.defineProperty(test, "a",{
    value:1,
    configurable:false,
    writable:false
})
delete test.a;
test.a=2
console.log(test);  //{a:1}
test.b=2
console.log(test); //{a:1,b:2}
Object.defineProperty(test, "a",{
    value:2,
}) //报错 Cannot redefine property
2.禁止扩展 Object.preventExtensions

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

let test={a:1}
Object.preventExtensions(test)
test.b=2
test.a=2
console.log(test); //{a:2}
Object.defineProperty(test, "a",{
    value:3,
})
console.log(test);  //{a:3}
Object.defineProperty(test, "b",{
    value:2,
}) //报错:Uncaught TypeError: Cannot define property b, object is not extensible

我们可以发现,通过preventExtensions,我们无法再新增(拓展)属性,只能更改原来存在的属性.

3.密封对象 Object.seal

Object.seal()方法用于密封一个对象,这个方法实际上会在一个现有对象上调用object.preventExtensions()并把所有现有属性标记为configurable:false.即将对象设置为不可扩展,同时将对象的所有自有属性都设置为不可配置(包括Symbol值的属性)。也就是说,不能给对象添加新的属性和方法,也不能删除现有的属性和方法、不能修改现有属性和方法的配置。但如果对象的属性和方法是可写的,那该属性和方法仍然可以修改。

    let test={a:1}
    Object.seal(test)
    test.a=2
    test.b=3
    console.log(test); //{a:2}
    delete test.a
    console.log(test); //{a:2}
    Object.defineProperty(test, "a",{
            value:3,
    })
    console.log(test); //{a:3}
    Object.defineProperty(test, "b",{
            value:3,
    }) //报错:Uncaught TypeError: Cannot define property b, object is not extensible
4.冻结对象 Object.freeze

Object.freeze()会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal(),并把所有现有属性标记为writable: false,这样就无法修改它们的值。

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

推荐阅读更多精彩内容