深刻浅出Object.defineProperty()

讲解大体会根据下图展开javascript


 
 

本文部分参考了书籍《你不知道的javascript》上卷java

对象的定义与赋值

常用的定义与赋值方法obj.prop =value或者obj['prop']=value
函数

 
 

 

Object.defineProperty()语法说明

Object.defineProperty()的做用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性ui

Object.defineProperty(obj, prop, desc) 
  1. obj 须要定义属性的当前对象
  2. prop 当前须要定义的属性名
  3. desc 属性描述符

通常经过为对象的属性赋值的状况下,对象的属性能够修改也能够删除,可是经过Object.defineProperty()定义属性,经过描述符的设置能够进行更精准的控制对象属性。spa

属性的特性以及内部属性

javacript 有三种类型的属性3d

  1. 命名数据属性:拥有一个肯定的值的属性。这也是最多见的属性
  2. 命名访问器属性:经过gettersetter进行读取和赋值的属性
  3. 内部属性:由JavaScript引擎内部使用的属性,不能经过JavaScript代码直接访问到,不过能够经过一些方法间接的读取和设置。好比,每一个对象都有一个内部属性[[Prototype]],你不能直接访问这个属性,但能够经过Object.getPrototypeOf()方法间接的读取到它的值。虽然内部属性一般用一个双吕括号包围的名称来表示,但实际上这并非它们的名字,它们是一种抽象操做,是不可见的,根本没有上面两种属性有的那种字符串类型的属性
属性描述符

经过Object.defineProperty()为对象定义属性,有两种形式,且不能混合使用,分别为数据描述符,存取描述符,下面分别描述下二者的区别:code

数据描述符 --特有的两个属性(value,writable)
let Person = {} Object.defineProperty(Person, 'name', { value: 'jack', writable: true // 是否能够改变 }) 
 
 

 
 

注意,若是描述符中的某些属性被省略,会使用如下默认规则:orm


 
 
存取描述符 --是由一对 getter、setter 函数功能来描述的属性

get:一个给属性提供getter的方法,若是没有getter则为undefined。该方法返回值被用做属性值。默认为undefined
set:一个给属性提供setter的方法,若是没有setter则为undefined。该方法将接受惟一参数,并将该参数的新值分配给该属性。默认值为undefined对象

let Person = {} let temp = null Object.defineProperty(Person, 'name', { get: function () { return temp }, set: function (val) { temp = val } }) 
 
 
数据描述符和存取描述均具备如下描述符
  1. configrable 描述属性是否配置,以及能否删除
  2. enumerable 描述属性是否会出如今for in 或者 Object.keys()的遍历中
configrable 代码片断分析
 
configurable:false不能删除属性

 
configurable:false不能从新定义属性

 
等价上一张图的代码

 
与上一张图的代码进行对比

 
configurable:true能删除属性

 
configurable:true可以定义属性

 
configurable:false与上图作对照

从以上代码运行结果分析总结可知:ip

  1. configurable: false 时,不能删除当前属性,且不能从新配置当前属性的描述符(有一个小小的意外:能够把writable的状态由true改成false,可是没法由false改成true),可是在writable: true的状况下,能够改变value的值
  2. configurable: true时,能够删除当前属性,能够配置当前属性全部描述符。
enumerable 代码片断分析
 
 

注意:如下二种区别


 
 

 
 
不变性
  1. 对象常量
    结合writable: false 和 configurable: false 就能够建立一个真正的常量属性(不可修改,不可从新定义或者删除)
 
对象常量
  1. 禁止扩展
    若是你想禁止一个对象添加新属性而且保留已有属性,就可使用Object.preventExtensions(...)
 
禁止扩展片断1

 
禁止扩展片断2

在非严格模式下,建立属性gender会静默失败,在严格模式下,将会抛出异常。

  1. 密封
    Object.seal()会建立一个密封的对象,这个方法实际上会在一个现有对象上调用object.preventExtensions(...)并把全部现有属性标记为configurable:false。
 
密封

因此, 密封以后不只不能添加新属性,也不能从新配置或者删除任何现有属性(虽然能够改属性的值)

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


     
    冻结

这个方法是你能够应用在对象上级别最高的不可变性,它会禁止对于对象自己及其任意直接属性的修改(可是这个对象引用的其余对象是不受影响的)
你能够深度冻结一个对象,具体方法为,首先这个对象上调用Object.freeze()而后遍历它引用的全部对象,并在这些对象上调用Object.freeze()。可是必定要当心,由于这么作有可能会无心中冻结其余共享对象。

属性定义和属性赋值

最后一小节,总结一下上述内容

属性定义,经过Object.defineProperty()形式
  1. 若是Obj没有名为Prop的自身属性的话:若是Obj是可扩展的话,则建立Prop这个自身属性,不然拒绝
  2. 若是Obj已经有了名为Prop的自身属性:则按照下面的步骤从新配置这个属性
  3. 若是这个已有的属性是不可配置的,则进行下面的操做会被拒绝
1: 将一个数据属性转换成访问器属性,反之变然 2: 改变`[[Configurable]]`或`[[Enumerable]]` 3: 改变[[Writable]]由false变为true 4: 在`[[Writable]]`为`false`时改变`[[Value]]` 5: 改变[[Get]]或[[Set]] 
  1. 不然这个已有的属性能够被从新配置
属性赋值,经过obj.prop = ''prop"形式
  1. 若是在原型链上存在一个名为P的只读属性(只读的数据属性或者没有setter的访问器属性),则拒绝
  2. 若是在原型链上存在一个名为P的且拥有setter的访问器属性,则调用这个setter
  3. 若是没有名为P的自身属性,则若是这个对象是可扩展的,就建立一个新属性,不然,若是这个对象是不可扩展的,则拒绝
  4. 若是已经存在一个可写的名为P的自身属性,则调用Object.defineProperty(),该操做只会更改P属性的值,其余的特性(好比可枚举性)都不会改变
做用以及影响

属性的定义操做和赋值操做各自有本身的做用和影响。
赋值可能会调用原型上的setter,定义会建立一个自身属性
原型链中的同名只读属性可能会阻止赋值操做,但不会阻止定义操做。若是原型链中存在一个同名的只读属性,则没法经过赋值的方式在原对象上添加这个自身属性,必须使用定义操做才能够。这项限制是在ECMAScript 5.1中引入的

 
 

 

 
 

 

 
 

 
 

赋值运算符不会改变原型链上的属性
不能经过为 obj.foo赋值来改变 proto.foo的值。这种操做只会在 obj上新建一个自身属性
 
 

对象字面量中的属性是经过定义操做添加的。
 
 

 

再次啰嗦一次,记住如下两种形式的区别:

 
 

上面的代码等同于:

 
 

另外一方面:

 
 

上面的代码等同于:

 
 

小礼

做者:Weastsea 连接:https://www.jianshu.com/p/8fe1382ba135 来源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。
相关文章
相关标签/搜索