講清楚之 javascript 對象屬性描述符

對象屬性形貌符

當他人對你說起對象屬性形貌符,可以會蒙逼。而假如說起對象屬性的 get/set 要領就秒懂了,規範形貌和習氣表述在這裡有些差別,然則指向的是同一個觀點所觸及的東西。對象屬性形貌符在編程實踐中是經由過程 Object 對象的defineProperty要領暴露給我們。所以搞清楚Object.defineProperty是明白對象屬性形貌符的唯一門路。

Object.defineProperty, define Property 翻譯成中文就是定義屬性,望文生義就是為對象定義或修正屬性的細節,即經由過程屬性形貌符來定義屬性讀寫的細節。運用該要領許可準確增加或修正對象的屬性,熟習 vue 的朋儕對 defineProperty 因該不生疏:

Object.defineProperty(obj, prop, descriptor)

defineProperty 接收3個參數, obj 示意要修正或許定義屬性的對象,prop 是要定義或許修正屬性的稱號, descriptor 屬性形貌符用於定義該屬性的特徵。

descriptor 是一個對象,對象里的屬性形貌符有兩種範例:數據形貌符存取形貌符

數據形貌符是一個具有值的屬性,該值多是可寫的,也可以不是可寫的。

存取形貌符是由getter-setter函數對形貌的屬性。形貌符必需是這兩種情勢之一;不能同時是二者。

數據形貌符和存取形貌符均具有一下可選鍵值(特徵):

  • configurable: 假如為 false,則任何嘗試刪除目的屬性或修正屬性以下特徵(writable, configurable, enumerable)的行動將被無效化,默許值為 false。
  • enumerable: 是否能羅列。也就是是否能被for-in遍歷。默許值為 false
  • writable: 是否能修正值。默以為 false
  • value: 該屬性的詳細值是多少。默以為 undefined

存取形貌符:

  • get: 目的屬性被接見就會調回此要領,並將此要領的運算效果返回用戶。默以為 undefined
  • set: 目的屬性被賦值,就會調回此要領。默以為 undefined

形貌符可同時具有的鍵值:

configurableenumerablevaluewritablegetset
數據形貌符YesYesYesYesNoNo
存取形貌符YesYesNoNoYesYes

假如一個形貌符不具有value,writable,get 和 set 恣意一個關鍵字,那末它將被以為是一個數據形貌符。假如一個形貌符同時有(value或writable)和(get或set)關鍵字,將會發生一個非常。所以 value、writable 與 get/set 不能同時設置。

var obj = {}
obj.a = 123

Object.defineProperty(obj, "newDataProperty", {
    value: 101, // 設置值
    writable: true, // 值可以被修正
    enumerable: true, // 可以被羅列
    configurable: true // 屬性可以被刪除、特徵可以修正
})

上面給對象 obj 增加一個新屬性 ‘newDataProperty’,而且設置了屬性的特徵。

在ES5之前對象的屬性我們只能設置一個字面量值或許一個援用,在瀏覽器支撐Object.defineProperty要領以後,就像給了我們一台顯微鏡,可以在更低的粒度層掌握屬性的行動和特徵:定義屬性的可接見行、值的讀寫劃定規矩等。

假如對象中不存在指定的屬性,Object.defineProperty()會建立這個屬性。假如屬性已存在,Object.defineProperty()將嘗試依據形貌符中的值以及對象當前的設置來修正這個屬性。假如舊形貌符將其configurable 屬性設置為false,則該屬性被以為是“不可設置的”,而且沒有屬性可以被轉變(除了單向轉變 writable 為 false)。當屬性不可設置時,不能在數據和接見器屬性範例之間切換。

形貌符中未顯現設置的特徵運用其默許值。

下面用幾個栗子來演示這些特徵的詳細表現:

configurable

let foo = {
    a: 1
}
delete foo.a
Object.defineProperty(foo, 'b', {
    value: 2, // 默許值為2
    configurable: false // 不容許被刪除和修正
})
delete foo.b // 沒法刪除
foo.b = 999 // 沒法修正
console.log(foo.b) // 2 

enumerable

let foo = {
    a: 1,
    b: 2,
    c: 3
}
for (let i in foo) {
    // a、b、c可以被羅列
    console.log(`key: ${i}, value: ${foo[i]}`)
}

Object.defineProperty(foo, 'a', {
    enumerable: false // 設置屬性不可以被羅列
})
for (let i in foo) {
    // a沒有被羅列
    console.log(`key: ${i}, value: ${foo[i]}`)
}

writable

let foo = {
    a: 1
}
// 修正 foo.a 的值
foo.a = 2 
console.log(foo.a) // 2

Object.defineProperty(foo, 'a', {
    writable: false // 設置值不能被修正
})
// 嘗試修正 foo.a 的值
foo.a = 3 // 沒法修正
console.log(foo.a) // 2

value

let foo = {}
Object.defineProperty(foo, 'a', {
    value: 1 // 設置屬性的值為 1
})
console.log(foo.a) // 1

get/set

let foo = {
    a: 1
}
Object.defineProperty(foo, 'b', {
    get: function () {
        return `hi, ${this.value}`
    },
    set: function (value) {
        this.a = value // 將輸入值保存在同對象下屬性 a 里
        this.value = value + 1
    }
})
console.log(foo.b) // 'hi, undefined'
foo.b = 1
console.log(foo.a) // 1
console.log(foo.b) // hi, 2

注重: get沒有參數,set接收實參為當前設置的值.。在get、set函數內部可以經由過程this.value接見value特徵,從而經由過程該特徵來獵取或許着設置屬性的值。get/set 常用於值依靠內部數據的場所。須要只管同時設置get、set。假如僅僅只設置了get,那末我們將沒法設置該屬性值。假如僅僅只設置了set,我們也沒法讀取該屬性的值。

Object.defineProperty只能設置一個屬性的形貌符,當須要設置多個屬性形貌符時可以運用Object.defineProperties

let foo = {}
Object.defineProperties(foo, {
    a: {
        value: 1,
        configurable: true
    },
    b: {
        get: function() {
            return this.value ? `hi, ${this.value}` : 0
        },
        set: function(value) {
            this.value = value + 1
        }
    }
})

console.log(foo.a) // 1
console.log(foo.b) // 0
foo.b = 2
console.log(foo.b) // 'hi, 3'

我們可以經由過程Object.getOwnPropertyDescriptor獵取某一屬性的特徵鳩合:

let foo = {
    a: 1
}
Object.defineProperty(foo, 'a', {
    value: 2, // 設置值為 2
    writable: false, // 值不可修正
    configurable: false // 設置屬性不可刪除,特徵不可修正
})
let fooDescripter = Object.getOwnPropertyDescriptor(foo, 'a')

console.log(fooDescripter)
// 獵取的特徵以下
// {
//   configurable:false,
//   enumerable:true,
//   value:2,
//   writable:false
// }

這裏須要注重,Object.defineProperty建立一個對象的新屬性與修正一個已存在屬性的區分。建立一個新屬性默許形貌符的鍵值都是 false 或許 undefined。而修正一個已存在的屬性的形貌符時,假如之前沒有被設置過或過原始體式格局給對象增加的屬性,則屬性的 configurable、enumerable、writable 形貌符都默以為 true。詳細差別舉個例子細細體味:

let foo = {}
Object.defineProperty(foo, 'a', {
    value: 2 // 設置值為 2
})
let fooDescripter = Object.getOwnPropertyDescriptor(foo, 'a')

console.log(fooDescripter)
// 獵取的特徵以下
// {
//   configurable:false, // 不容許被刪除和修正
//   enumerable: false, // 不能被羅列
//   value:2,
//   writable:false // 值不可修正
// }

變量 a 是經由過程 Object.defineProperty要領建立的,默許一切屬性形貌符的值都為 false。 我們可以經由過程末了兩個代碼示例體味一下區分:enumerable屬性形貌符在兩個例子中都沒有被事前設置,然則差別情況下的值不一樣。

原則上這個系列不會去講某個API,然則屬性形貌符可以加深我們對 javascript 、框架底層的明白。

    原文作者:kooky
    原文地址: https://segmentfault.com/a/1190000015206927
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞