Object.defineProperty()講解

由可枚舉屬性引起的

JavaScript屬性分為可枚舉屬性和不可枚舉屬性常拓?什么是“枚舉”,它又有什么用呢?

var apple = {
  name : "蘋果",
  price :18
}
for(var prop in obj){
    console.log(prop)
}
// 輸出:name ,price
// 這里的可枚舉性就是說for的這種寫法可以得到這個(gè)對(duì)象的屬性名
 
var stu = {};
Object.defineProperties(stu, {
    name: {
        value: "張三",
        enumerable: false
    },
    age: {
        value: 18,
        enumerable: true
    }
});
 
for(var prop in stu){
    console.log(prop);
}
// 輸出: age
//name屬性不能便利出來厂汗,因?yàn)樗?“enumerable” 值為false
 
//以上代碼請(qǐng)?jiān)赾hrome或者火狐里面運(yùn)行,IE9以下運(yùn)行第二段代碼會(huì)出錯(cuò)

我們知道呜师,for...in語句以任意順序遍歷一個(gè)對(duì)象的可枚舉屬性娶桦。
既 stu.age 可枚舉,stu.name 不可枚舉,而他們是否可枚舉是通過 enumerable 來設(shè)置的衷畦。

屬性描述符

我們通常會(huì)直接創(chuàng)建對(duì)象栗涂,然后設(shè)置對(duì)象的屬性,例如上面的 obj 對(duì)象霎匈,設(shè)置了 name ,price 屬性戴差。其實(shí)這些屬性也有一定的限定的,這些限定屬性性質(zhì)的我們稱為“屬性描述符”铛嘱。
這里拿 price 屬性來說暖释,我們能輸出 fruit.price 等于 18 ,其實(shí)是通過 price 的描述符 value 來設(shè)置的墨吓。例如上面的stu.age 的設(shè)置球匕;
price 也有“enumerable”描述符,他之所以能被 for in 到 ,是因?yàn)閷?duì)自身添加屬性的 “enumerable” 默認(rèn)為 true帖烘。

除了上面說到過的 “value” 和 “enumerable” 還有什么描述符呢亮曹?

每個(gè)屬性都有
Configurable描述符、Enumerable描述符秘症、Writable描述符照卦、Value描述符
或者,每個(gè)屬性都有
Configurable描述符乡摹、Enumerable描述符役耕、Get描述符、Set描述符
前者稱為 數(shù)據(jù)描述符聪廉,他們的值決定了該js對(duì)象的屬性的某些性質(zhì)及是否可讀寫瞬痘。
后者稱為 讀取描述符,他們的值決定了該js對(duì)象的屬性的某些性質(zhì)及讀寫的行為板熊。

描述符必須是這兩種形式之一框全,不能同時(shí)是兩者。


那么這些屬性是各代表著什么干签?
數(shù)據(jù)描述符具有以下鍵值

值(默認(rèn)值) 作用
configurable false --------------------- 為 true 時(shí)津辩,該屬性描述符才能夠被改變,表示對(duì)象的屬性是否可以被刪除筒严,以及除writable特性外的其他特性是否可以被修改丹泉。
enumerable false true時(shí),該屬性在對(duì)象中才是可枚舉的
value undefined 該屬性對(duì)應(yīng)的值鸭蛙∧『蓿可以是任何有效的 JavaScript 值(數(shù)值,對(duì)象娶视,函數(shù)等)晒哄。讀取屬性的時(shí)候就是通過這里開始讀
writable false 表示能否修改屬性的值賦值運(yùn)算符(assignment operator)基于右值(right operand)的值睁宰,給左值(left operand)賦值。")改變

讀取描述符具有以下鍵值

值(默認(rèn)值) 作用
configurable false --------------------- 為 true 時(shí)寝凌,該屬性描述符才能夠被改變
enumerable false true時(shí)柒傻,該屬性在對(duì)象中才是可枚舉的
get undefined 在讀取屬性時(shí)調(diào)用的函數(shù)
set undefined 在設(shè)置屬性時(shí)調(diào)用的函數(shù)

知道這些描述符所控制的性質(zhì),那又是什么時(shí)候去哪里設(shè)置的呢较木?這就關(guān)乎到Object.defineProperty() 了红符,它有三個(gè)參數(shù),Object.defineProperty(obj, prop, descriptor)伐债,其中descriptor就是設(shè)置屬性性質(zhì)描述符预侯。

defineProperty 和 defineProperties
  1. 語法
    Object.defineProperty(obj, prop, descriptor)
    Object.defineProperties(obj, props)
  2. 定義:
    Object.defineProperty() 直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改現(xiàn)有屬性峰锁,并返回該對(duì)象萎馅。
    Object.definePropertys() 直接在一個(gè)對(duì)象上定義一個(gè)或多個(gè)新的屬性,或者修改現(xiàn)有屬性虹蒋,并返回該對(duì)象糜芳。
  3. 參數(shù)
    obj 要在其上定義屬性的對(duì)象。
    prop 要定義或修改的屬性的名稱魄衅。
    descriptor 將被定義或修改的屬性描述符峭竣。
  4. 返回值
    返回被操作的對(duì)象,即返回obj參數(shù)
  5. 注意點(diǎn)
    1)當(dāng)把configurable值設(shè)置為false后,就不能修改任何屬性了,包括自己本身這個(gè)屬性
    2)想用訪問器屬性模擬默認(rèn)行為的話,必須得在里面新頂一個(gè)屬性,不然的話會(huì)造成循環(huán)引用
    3)可枚舉屬性 對(duì)for/in, Object.keys(), JSON.stringify(), Object.assign() 方法才生效(for/in 是對(duì)所有可枚舉屬性晃虫,而其他三種是對(duì)自身可枚舉屬性)
  6. 用途
    1)vue通過getter-setter函數(shù)來實(shí)現(xiàn)雙向綁定
    2)俗稱屬性掛載器
    3)專門監(jiān)聽對(duì)象數(shù)組變化的Object.observe()(es7)也用到了該方法



知道了Object.defineProperty()這個(gè)東東是用來生成或修改一個(gè)對(duì)象屬性邪驮,知道對(duì)象屬性的性質(zhì)是靠descriptor這個(gè)參數(shù)來設(shè)置之后,我們來看看他是怎么運(yùn)用的傲茄。
var person = {}為例,我們要怎樣去修改默認(rèn)的屬性值呢沮榜?

  • 設(shè)置該屬性為數(shù)據(jù)描述符
var person = {}

Object.defineProperty(person,'a',{
    configurable:true, //可修改默認(rèn)屬性
    enumerable:true, //可枚舉
    writable:true, //可修改這個(gè)屬性的值
    value:1 //定義一個(gè)初始的值為1
})

console.log(person) //{a: 1}
person.a=2
console.log(person) //{a: 2}

for(var k in person){
    console.log(k) //a
}

現(xiàn)在我們來修改下enumerable和writable值

Object.defineProperty(person,'a',{
    configurable:true,
    enumerable:false,
    writable:false,
    value:1
})

console.log(person) //{a: 1}
person.a=2
console.log(person) //{a: 1} 因?yàn)閣ritable值被設(shè)置為false了,所以不可以寫,嚴(yán)格模式下會(huì)報(bào)錯(cuò)

for(var k in person){
    console.log(k)// 沒有可枚舉屬性,因?yàn)閍的enumerable的值被設(shè)置為false
}

我們?cè)囋嚢蒫onfigurable的值改為false

Object.defineProperty(person,'a',{
    configurable:false,//為false的時(shí)候不允許修改默認(rèn)屬性了
})
===============================
# 改為false之后再試試修改其他屬性
Object.defineProperty(person,'a',{
    configurable:true,
    enumerable:true,
    writable:true,
    value:1
})
//woa,控制臺(tái)直接報(bào)錯(cuò)了!連想把false值改回true都不行!也就是說,這個(gè)改動(dòng)是一次性了!
//也就是說,你可以使用Object.defineProperty()方法無限修改同一個(gè)屬性,但是當(dāng)把configurable改為false之后就什么都不能再修改了


  • 設(shè)置該屬性為讀取描述符
var person = {
    a:1
}
Object.defineProperty(person,'a',{
    get(){
        return 3 //當(dāng)訪問這個(gè)屬性的時(shí)候返回3
    },
    set(val){
        console.log(val)//當(dāng)設(shè)置這個(gè)屬性的時(shí)候執(zhí)行,val是設(shè)置的值
    }
})

person.a// 3,我們明明寫的是a:1,怎么返回的3呢?這就是get()的威力了
person.a = 5// 5,相應(yīng)的設(shè)置的時(shí)候執(zhí)行了set()函數(shù)

我們來模擬一個(gè)訪問和設(shè)置的默認(rèn)行為

var person = {
    a:1
}
// 注:里面的this指向ogj(person)
Object.defineProperty(person,'a',{
    get(){
        return this.a 
    },
    set(val){
        this.a = val 
    }
})
//我們想當(dāng)然的這么寫.
person.a//Uncaught RangeError: Maximum call stack size exceeded

什么,溢出了?這是為什么?
哦~原來是這么寫的話會(huì)造成循環(huán)引用,狂call不止
我們看下流程:
person.a → get.call(person) → this.a → person.a → get.call(person) → this.a......
我們來修改下

var person = {
    a:1
}
Object.defineProperty(person,'a',{
    get(){
        return this._a || 1 //定義一個(gè)新的屬性和一個(gè)默認(rèn)值
    },
    set(val){
        this._a = val 
    }
})
person.a// 1
person.a=2// 2
person.a// 2
這樣就好了

這就是數(shù)據(jù)描述符和讀取描述符的應(yīng)用方式盘榨。在平時(shí)簡單的開發(fā)中可能用不上,但是知道了這些之后對(duì)一些框架的封裝的理解還是很有幫助的蟆融,例如vue數(shù)據(jù)雙向綁定原理上利用的就是Object.defineProperty方法草巡。



拓展

每個(gè)對(duì)象都有的一些方法:

  • Object.getOwnPropertyDescriptors(obj)
    定義:獲取一個(gè)對(duì)象的所有自身屬性(自身屬性)的描述符
    使用:Object.getOwnPropertyDescriptors(apple)

  • Object.getOwnPropertyDescriptor(obj, prop)
    定義:返回指定對(duì)象上一個(gè)自有屬性對(duì)應(yīng)的屬性描述符
    使用:Object.getOwnPropertyDescriptor(apple, 'price')


Object.getOwnPropertyDescriptor 的應(yīng)用:
一般直接添加屬性時(shí),屬性描述符默認(rèn)值都為 true型酥,當(dāng)用 Object.defineProperty() 方法來添加對(duì)象屬性時(shí)山憨,此時(shí)的屬性描述符默認(rèn)值為false
以文章開頭的 apple 和 stu 的案例:

// 直接添加屬性
Object.getOwnPropertyDescriptor(apple, 'name')
//name:{value: "蘋果", writable: true, enumerable: true, configurable: true}
// 屬性描述符添加屬性
Object.getOwnPropertyDescriptor(stu, 'name')
//name:{value: "張三", writable: false, enumerable: false, configurable: false}


// 又或者
var stu2 = Object.create({}, { name: { value: '李四' } })
Object.getOwnPropertyDescriptor(stu2, 'name')
//name:{value: "李四", writable: false, enumerable: false, configurable: false}

Object.create() 的第二個(gè)參數(shù)為 Object.defineProperties 的第二個(gè)參數(shù),既設(shè)置屬性及屬性描述符弥喉, 詳情移步到 Object.create()


參考資料:
1.MDN
3.關(guān)于 Object.defineProperty() 小結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末郁竟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子由境,更是在濱河造成了極大的恐慌棚亩,老刑警劉巖蓖议,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異讥蟆,居然都是意外死亡勒虾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門瘸彤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來修然,“玉大人,你說我怎么就攤上這事质况°邓危” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵拯杠,是天一觀的道長掏婶。 經(jīng)常有香客問我,道長潭陪,這世上最難降的妖魔是什么雄妥? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮依溯,結(jié)果婚禮上老厌,老公的妹妹穿的比我還像新娘。我一直安慰自己黎炉,他們只是感情好枝秤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著慷嗜,像睡著了一般淀弹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上庆械,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天薇溃,我揣著相機(jī)與錄音,去河邊找鬼缭乘。 笑死沐序,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的堕绩。 我是一名探鬼主播策幼,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼奴紧!你這毒婦竟也來了特姐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤黍氮,失蹤者是張志新(化名)和其女友劉穎到逊,沒想到半個(gè)月后铣口,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡觉壶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年脑题,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铜靶。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叔遂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出争剿,到底是詐尸還是另有隱情已艰,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布蚕苇,位于F島的核電站哩掺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏涩笤。R本人自食惡果不足惜嚼吞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蹬碧。 院中可真熱鬧舱禽,春花似錦、人聲如沸恩沽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罗心。三九已至里伯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渤闷,已是汗流浹背俏脊。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肤晓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓认然,卻偏偏與公主長得像补憾,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卷员,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容

  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持盈匾,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠毕骡,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 2,595評(píng)論 9 22
  • 概述 JavaScript提供了一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)削饵,用來描述一個(gè)對(duì)象的屬性的行為岩瘦,控制它的行為。這被稱為“屬性描述對(duì)...
    zjh111閱讀 726評(píng)論 0 0
  • 什么是屬性描述符 創(chuàng)建一個(gè)對(duì)象最常用的方式是聲明的形式窿撬,大概語法是這樣的: 也可以采用構(gòu)造形式启昧,創(chuàng)建一個(gè)Objec...
    冰果2016閱讀 2,896評(píng)論 0 5
  • 本篇主要介紹JS中常用Object的屬性方法。 delete 操作 in 運(yùn)算符 obj.hasOwnProper...
    boySpray閱讀 1,995評(píng)論 0 2
  • 《射雕英雄傳》這部電視劇劈伴,以前看過一次密末,至于以前有多前,已經(jīng)記不清楚跛璧。大概是小學(xué)严里,或許是初中,高中確定是沒看過追城。也...
    臭小媽媽閱讀 209評(píng)論 0 0