簡單的理解 Object.defineProperty()

正在追逐的萌新發表於2021-12-17

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

Object.defineProperty(obj,prop,descriptor)
obj 要在其定義屬性的物件
prop 要定義或者修改的屬性的名稱
descriptor 將被定義或修改的屬性描述符

通過Object.defineProperty()為物件定義屬性,有兩種形式,且不能混合使用,分別為資料描述符,存取描述符。
下面分別描述兩者的區別:

資料描述符 -- 特有的兩個屬性(value,writable)
value 屬性值
writable 是否可以改變屬性的值。預設為false,不能改變。

    let obj = {}
    Object.defineProperty(obj, 'name', {
        value: 'jack',
        writable: false // 不能改變屬性的值
    })
    obj.name = '123'
    console.log(obj)// {name: 'jack'}

 

    let obj = {}
    Object.defineProperty(obj, 'name', {
        value: 'jack',
        writable: true // 可以改變屬性的值
    })
    obj.name = '123'
    console.log(obj)// {name: 123}

 

 

存取描述符 -- 是由一對 getter、setter 函式功能來描述的屬性
get:一個給屬性提供getter的方法,如果沒有getter則為undefined。該方法返回值被用作屬性值。預設為undefined。
set:一個給屬性提供setter的方法,如果沒有setter則為undefined。該方法將接受唯一引數,並將該引數的新值分配給該屬性。預設值為undefined。

    let obj = {}
    let temp = null
    Object.defineProperty(obj, 'name', {
        get: function (val) {
            return temp
        // console.log("set方法被呼叫"+val);
        },
        set: function (val) {
            temp = val
            console.log("get方法被呼叫" + val);
        }
    })
    obj.name = 'chen'
    console.log(obj.name) //chen

 

 

資料描述符和存取描述都具有以下描述符:
configrable 描述屬性是否配置,以及可否刪除
enumerable 描述屬性是否會出現在for in 或者 Object.keys()的遍歷中

configrable
單獨設定:configurable: false 不能刪除屬性、不能重新定義屬性
單獨設定:configurable: true 能刪除屬性

同時設定:configurable:true 和 writable:false 可以修改屬性值
注意:通過賦值的形式,不可以修改,因為 writable為false

    let obj = {}
    Object.defineProperty(obj,'name',{
        value: 'chen',
        configurable: true,
        writable: false
    })
    Object.defineProperty(obj,'name',{
        value: 'rose'
    })
    //通過屬性定義的形式可以修改name的屬性值
    console.log(obj.name)
    //通過賦值的形式,不可以修改,因為 writable為false
    obj.name = 'JS'
    console.log(obj.name)// rose

 

 

同時設定:configurable:false 和 writable:true 可以修改屬性值
注意:通過賦值的形式,可以修改,因為 writable為true

    let obj = {}
    Object.defineProperty(obj,'name',{
        value: 'chen',
        configurable: false,
        writable: true
    })
    Object.defineProperty(obj,'name',{
        value: 'rose'
    })
    //通過屬性定義的形式可以修改name的屬性值
    console.log(obj.name) // rose
    //通過賦值的形式,可以修改,因為 writable為 true
    obj.name = 'JS'
    console.log(obj.name)// JS

 

 

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

enumerable 看如下程式碼

    let obj = {}
    Object.defineProperty(obj,'name',{
        value: 'chen',
        enumerable: false
    })

    obj.gender = 'HTML'

    Object.defineProperty(obj,'age',{
        value: '110',
        enumerable: true
    })

    console.log(Object.keys(obj)) //['gender', 'age']

    for (let i in obj) {
        console.log(i)// gender,age
    }

    console.log(obj.propertyIsEnumerable('name')) // false
    console.log(obj.propertyIsEnumerable('gender')) // true
    console.log(obj.propertyIsEnumerable('age')) // true

 

注意:下面程式碼的區別

    let obj = {}
    obj.gender = 'HTML' // 等價於下面的程式碼
    Object.defineProperty(obj,'gender',{ // 等於這裡
        value: 'HTML',
        configurable: true,
        writable: true,
        enumerable:true
    })

    Object.defineProperty(obj,'age',{ // 等價於下面的程式碼
        value: '120'
    })
    Object.defineProperty(obj,'age',{ // 等於這裡
        value: '120',
        configurable: false,
        writable: false,
        enumerable: false
    })

 


不變性
1、物件常量
結合writable: false 和 configurable: false 就可以建立一個真正的常量屬性(不可修改,不可重新定義或者刪除)

    let obj = {}
    Object.defineProperty(obj,'name',{
        value: 'HTML',
        configurable: false,
        writable: false,
    })
    delete obj.name //不可刪除
    obj.name = 'JS' //不可以重新賦值
    //通過賦值,可以新增新屬性
    obj.gender = 'CSS'
    console.log(obj.gender) // CSS
    //不可重新定義
    Object.defineProperty(obj,'name',{value: 'chen'})// 報錯: Cannot redefine property: name

 

 

2、禁止擴充套件
如果你想禁止一個物件新增新屬性並且保留已有屬性,就可以使用 Object.preventExtensions(...)

禁止擴充套件片段一:

   'use strict'
    var obj = {name: 'JS'}
    Object.preventExtensions(obj)
    obj.gender = 'CSS'
    console.log(obj.gender) // Cannot add property gender, object is not extensible

 

 

禁止擴充套件片段二:

    var obj = {name: 'JS'}
    Object.preventExtensions(obj) // 禁止擴充套件
    // 但是仍然可以進行配置
    Object.defineProperty(obj, 'name',{
        value: 'HTML',
        writable: false,
        configurable: false
    })
    console.log(obj.name) // HTML
    //不能進行擴充套件
    obj.gender = 'CSS'
    console.log(obj.gender) //undefined

 

 

注意:在非嚴格模式下,建立屬性gender會靜默失敗,在嚴格模式下,將會丟擲異常。

 

3、密封
Object.seal()會建立一個密封的物件,這個方法實際上會在一個現有物件上呼叫object.preventExtensions(...)並把所有現有屬性標記為configurable:false。

    var obj = {
        name: 'HTML'
    }
    Object.seal(obj)
    obj.gender = 'CSS'
    // 不能擴充套件屬性
    console.log(obj.gender)// undefined
    // 再次驗證
    console.log(Object.keys(obj)) // ['name']
    // 不能再次配置屬性
    Object.defineProperty(obj,'name',{ // Cannot redefine property: name
        name: 'JS',
        configurable: true
    })

 

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

 

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

     var obj = {
        name: 'HTML'
    }
    Object.freeze(obj)
    obj.name = 'Chen'
    // 不可以修改已有屬性的值
    console.log(obj.name) // HTML

 


這個方法是可以應用在物件上級別最高的不可變性,它會禁止對於物件本身及其任意直接屬性的修改(但是這個物件引用的其他物件是不受影響的)
你可以深度凍結一個物件,具體方法為,首先這個物件上呼叫Object.freeze()然後遍歷它引用的所有物件,並在這些物件上呼叫Object.freeze()。
但是一定要小心,因為這麼做有可能會無意中凍結其他共享物件。

 

var obj = {
name: 'HTML'
}
Object.freeze(obj)
obj.name = 'Chen'
// 不可以修改已有屬性的值
console.log(obj.name) // HTML

相關文章