深刻浅出Object.defineProperty()

转载:https://www.jianshu.com/p/8fe1382ba135


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

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

对象的定义与赋值

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

Object.defineProperty()语法说明

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

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

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

属性的特性以及内部属性

javacript 有三种类型的属性ui

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

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

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


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


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

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

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与上图作对照

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

  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上新建一个自身属性


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

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

上面的代码等同于:

另外一方面:

上面的代码等同于:


转载自:https://www.jianshu.com/p/8fe1382ba135,用于之后学习使用!
相关文章
相关标签/搜索