VUE2.x原理之Object.defineProperty()


Object.defineProperty()語法說明

Object.defineProperty()的作用就是直接在一個對象上定義一個新屬性,或者修改一個已經存在的屬性

Object.defineProperty(obj, prop, desc)

obj 需要定義屬性的當前對象

prop 當前需要定義的屬性名

desc 屬性描述符

一般通過為對象的屬性賦值的情況下,對象的屬性可以修改也可以刪除,但是通過Object.defineProperty()定義屬性,通過描述符的設置可以進行更精准的控制對象屬性。

屬性描述符

通過Object.defineProperty()為對象定義屬性,有兩種形式,且不能混合使用,分別為數據描述符,存取描述符,下面分別描述下兩者的區別:value和 get 是同一個作用,只能同時用一個。 writable和set是同一個作用,用一個。

數據描述符 --特有的兩個屬性(value,writable)

let Person = {}
Object.defineProperty(Person, 'name', {
   value: 'jack',
   writable: true // 是否可以改變
})

注意,如果描述符中的某些屬性被省略,會使用以下默認規則:

 

存取描述符 --是由一對 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
  }
})
Person.name = 'hy'
console.log('Person.name') //hy

數據描述符和存取描述均具有以下描述符

configrable 描述屬性是否配置,以及可否刪除

enumerable 描述屬性是否會出現在for in 或者 Object.keys()的遍歷中

 

 1 、configurable:false不能刪除屬性

    let Person = {}
    Object.defineProperty(Person, 'name', {
        value:'hy',
        configurable:false
    })
    delete Person.name
    console.log(Person)//{Person:'hy'}

2、configurable:false不能重新定義屬性

  let Person = {}
    Object.defineProperty(Person, 'name', {
        value:'hy',
        configurable:false
    })
    Object.defineProperty(Person, 'name', {
        value:'dfj',
    })
    console.log(Person)//Cannot redefine property: name

3、可以通過屬性定義的形式修改name的值

   let Person = {}
    Object.defineProperty(Person, 'name', {
        value:'hy',
        configurable:true,
        writable:false
    })
    Object.defineProperty(Person, 'name', {
        value:'dfj',
    })
    console.log(Person)//{name: "dfj"}

4、當writable為true時,無論configuttable是什么,都可用過屬性定義形式或直接賦值形式改變name值

  let Person = {}
    Object.defineProperty(Person, 'name', {
        value:'hy',
        configurable:false,
        writable:true
    })
    Object.defineProperty(Person, 'name', {
        value:'dfj',
    })
    console.log(Person)//{name: "dfj"}
    Person.name = 'xh'
    console.log(Person)//{name: "xh"}

總結:

  • configurable: false 時,不能刪除當前屬性,且不能重新配置當前屬性的描述符(有一個小小的意外:可以把writable的狀態由true改為false,但是無法由false改為true),但是在writable: true的情況下,可以改變value的值
  • configurable: true時,可以刪除當前屬性,可以配置當前屬性所有描述符

enumerable 代碼片段分析

1、當enumerable為true時,對象中的屬性可枚舉,反之則不能

let Person = {}
    Object.defineProperty(Person, 'name', {
        value: 'hy',
        enumerable:true
    })
    Object.defineProperty(Person, 'age', {
        value: '25',
        enumerable:false
    })
    Object.defineProperty(Person, 'sex', {
        value: 'man',
        enumerable:true
    })
    Person.hobby = 'coding'
    for(let i in Person){
        console.log(i) //name 、 sex 、 hobby
    }
    console.log(Object.keys(Person)) //["name", "sex", "hobby"]

 

不變形

1、對象常量:結合writable: false 和 configurable: false 就可以創建一個真正的常量屬性(不可修改,不可重新定義或者刪除)
 let Person = {}
    Object.defineProperty(Person, 'name', {
        value: 'hy',
        writable:false,
        configurable:false
    })
    delete Person.name
    Person.name = 'xh'
    Person.age = '14' //可以設置新的屬性值
    console.log(Person)//{age: "14", name: "hy"}

2、禁止擴展:如果你想禁止一個對象添加新屬性並且保留已有屬性,就可以使用Object.preventExtensions(...)

   'use strict' //非嚴格模式下或創建失敗,嚴格模式下會報錯
    let Person = { name : 'xh'}
    Object.preventExtensions(Person)
    Object.defineProperty(Person, 'name', {
        value: 'hy',
        writable:false,
        configurable:false
    })
    console.log(Person)
    Person.age = '14'
    console.log(Person)// Cannot add property age, object is not extensible

3、密封Object.seal()會創建一個密封的對象,這個方法實際上會在一個現有對象上調用object.preventExtensions(...)並把所有現有屬性標記為configurable:false。

   'use strict'
    let Person = { name : 'xh'}
    Object.seal(Person) //這一步相當於調用了preventExtensions 禁止添加新的屬性,且將原有屬性configurable改為false 則不能配置,但可以修改
    Object.defineProperty(Person, 'name', {
        value: 'hy',
        configurable:true
    })
    console.log(Person)//Cannot redefine property: name

所以, 密封之后不僅不能添加新屬性,也不能重新配置或者刪除任何現有屬性(雖然可以改屬性的值)

  'use strict'
    let Person = { name : 'xh'}
    Object.seal(Person)
    Person.name = 'dfj' //改屬性值(修改)
    console.log(Person)
    Object.defineProperty(Person, 'name', {
        value: 'hy',
        configurable:true
    })
    console.log(Person)//Cannot redefine property: name

4、Object.freeze()會創建一個凍結對象,這個方法實際上會在一個現有對象上調用Object.seal(),並把所有現有屬性標記為writable: false,這樣就無法修改它們的值

    'use strict'
    let Person = { name : 'xh'}
    Object.freeze(Person) //調用了seal導致不能配置刪除 又將writable改為false 導致不能修改
    Person.name = 'dfj' //Cannot assign to read only property 'name
這個方法是你可以應用在對象上級別最高的不可變性,它會禁止對於對象本身及其任意直接屬性的修改(但是這個對象引用的其他對象是不受影響的)
你可以深度凍結一個對象,具體方法為,首先這個對象上調用Object.freeze()然后遍歷它引用的所有對象,並在這些對象上調用Object.freeze()。但是一定要小心,因為這么做有可能會無意中凍結其他共享對象。
 
 

總結:

屬性定義,通過Object.defineProperty()形式

如果Obj沒有名為Prop的自身屬性的話:如果Obj是可擴展的話,則創建Prop這個自身屬性,否則拒絕

如果Obj已經有了名為Prop的自身屬性:則按照下面的步驟重新配置這個屬性

如果這個已有的屬性是不可配置的,則進行下面的操作會被拒絕

1: 將一個數據屬性轉換成訪問器屬性,反之變然
2: 改變`[[Configurable]]`或`[[Enumerable]]`
3: 改變[[Writable]]由false變為true
4: 在`[[Writable]]`為`false`時改變`[[Value]]`
5: 改變[[Get]]或[[Set]]

否則這個已有的屬性可以被重新配置

屬性賦值,通過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上新建一個自身屬性
 
 

對象字面量中的屬性是通過定義操作添加的。
 
 

注意:Object.defineProperties()和Object.defineProperty()是不一樣的方法

 

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。

語法:Object.defineProperty(objpropdescriptor)

參數:

  obj(將要被添加屬性或修改屬性的對象) 

  prop(與第一個對象中要添加的屬性一一對應) 

  descriptor(將被定義或修改的屬性的描述符)

Object.defineProperties() 方法直接在一個對象上定義新的屬性或修改現有屬性,並返回該對象。

語法:Object.defineProperties(objprops)

參數:

  obj(將要被添加屬性或修改屬性的對象) 

  prop(與第一個對象中要添加的屬性一一對應) 

var obj = {};
Object.defineProperties(obj, {
  "property1": {
    value: true,
    writable: true
  },
  "property2": {
    value: "Hello",
    writable: false
  }
  // 等等.
});
alert(obj.property2) //彈出"Hello" 

 

再次啰嗦一次,記住以下兩種形式的區別:

 
 

上面的代碼等同於:

 
 

另一方面:

 
 

上面的代碼等同於:

 
 
 
 

 
 


參考文獻:https://www.jianshu.com/p/8fe1382ba135

 



 




 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM