摘要:但好在還給我們提供了一個方法,每一個對象都有這樣一個方法,專門用來判斷某個屬性是否是該對象的私有屬性。如果你想要用對象字面形式,你只能在創(chuàng)建對象時定義訪問器屬性。在中,我們使用凍結(jié)一個對象,并且使用來判斷一個對象是否被凍結(jié)。
說完了對象那些不常用的冷知識,是時候來看看JavaScript中對象屬性有哪些有意思的東西了。
不出你所料,對象屬性自然也有其相應的特征屬性,但是這個話題有點復雜,讓我們先從簡單的說起,對象屬性的分類。
面對一個復雜的事物,尋找其內(nèi)在共性,妥善分類往往是快速認知該事物的捷徑,這與程序員“將難以解決的大問題拆解為可以解決的小問題”的思維有異曲同工之妙。
那么,對象的屬性根據(jù)不同的維度,可以如何分類呢?你或許想不到,竟然有如此多的分類方法,而不同的類別,有牽扯出特定的方法解決這一類別的某些問題。讓我們看看吧:
按來源分類私有屬性
原型屬性
JavaScript是一門基于原型鏈的語言,對象繼承是節(jié)省內(nèi)存空間,避免代碼重復,邏輯混亂的好方法。而對象繼承對于屬性而言則帶來一個問題,即我們需要區(qū)分某對象內(nèi)的屬性,究竟是對象自有的(私有屬性),還是繼承于其他對象(繼承屬性),無論是進行屬性遍歷還是對屬性進行操作,我們都需要謹慎的思考這個問題。
讓我們舉例兩個典型場景看看JavaScript是如何幫助我們解決這個問題的:
情景一: 屬性查找有時候,我們需要查找某個對象是否有某個屬性,再進一步?jīng)Q定是否要執(zhí)行下一步操作,JavaScript提供給我們的查找工具是in操作符,in操作符用以在給定對象中查找一個給定名稱的屬性,如果找到則返回true值。實際上,in操作符就是在哈希表中查找一個鍵是否存在(還記的我們的藍色章魚嗎,in操作符只是檢查章魚是否有那只觸角,并不關(guān)心觸角上拿著的卡片上寫了什么,更不會隨著卡片的地址去讀取值,這對于提升性能尤其重要),讓我們看看代碼示例:
let obj = { x: 1, } console.log("x" in obj) // true console.log("y" in obj) // false
但是遺憾的是,in操作符會檢查所有的私有屬性和原型屬性,因此你并不能通過in操作符知道該屬性的真正來源。
但好在JavaScript還給我們提供了一個.hasOwnProperty()方法,每一個對象都有這樣一個方法,專門用來判斷某個屬性是否是該對象的私有屬性。
我們終于得到了我們想要的。太棒了。
小結(jié):
查找屬性(不區(qū)分屬性來源):in操作符
查找私有屬性:對象的.hasOwnProperty()方法
情景二: 屬性枚舉有些時候,我們想要獲得一個對象內(nèi)所有屬性的鍵或值(或者全部都要),這時我們就要枚舉一個對象內(nèi)的所有屬性,通常,我們會使用for-in循環(huán)去實現(xiàn)這一點。
然而,很不巧的是,for-in循環(huán)會遍歷所有可枚舉的原型屬性,注意這里有兩點需要進一步說明:
可枚舉:這牽扯到我們很快要談到的屬性特征屬性(有點拗口是吧:))
會遍歷原型屬性:這樣當一個對象的繼承鏈很長而我們又只關(guān)心對象的私有屬性時就會變得非常麻煩
當然,你可以在for-in循環(huán)中,再使用我們剛提到的.hasOwnProperty()方法,但是JavaScript給予了我們更好的選擇:使用Object.keys()方法:
Object.keys()方法是ECMAScript5引入的方法,它可以獲取可枚舉屬性的名字的數(shù)組,并且它只返回對象的自有屬性。
因此,你可以基于是否需要一個數(shù)組,是否只需要對象自有屬性來判斷使用哪一種方法。
小結(jié):
枚舉屬性(不區(qū)分屬性來源):for-in循環(huán)
只枚舉私有屬性,且返回數(shù)組:Object.keys()方法
按作用分類數(shù)據(jù)屬性
訪問器屬性
你也許很少聽說過這樣的分類方式,因為我們幾乎都在使用數(shù)據(jù)屬性,讓我來簡要說明這兩種類型的屬性的區(qū)別:
數(shù)據(jù)屬性包含了一個值,我們之前提到的對象的內(nèi)部方法[[Put]]的默認行為就是創(chuàng)建數(shù)據(jù)屬性:
let obj = { x: 1, // x 是數(shù)據(jù)屬性 }
訪問器屬性不包含值,而是定義了一個當屬性被讀取時調(diào)用的函數(shù)(稱為“getter”)和一個當屬性被寫入時調(diào)用的函數(shù)(稱為“setter”):
let obj = { x: 1, get y() { return 2 } set y() { return 3 } } console.log(obj.y) // 2
之所以訪問器屬性很少見到是因為我們很少需要在進行屬性賦值或讀取操作時觸發(fā)一些行為,不過反過來說,如果這恰恰是你面臨的場景,就大膽的使用吧。
對象屬性的特征屬性繞了一大圈,終于可以回到正題,談談屬性的特征屬性了,相較于對象只有一個孤零零的[[Extensiable]]特征屬性,對象屬性要復雜的多:
因為所有對象屬性都具有:
[[Enumerable]]特征屬性:決定一個屬性是否可以被遍歷;
[[Configurable]]特征屬性:決定一個屬性是否可以被配置
而只有數(shù)據(jù)屬性有以下兩個屬性:
[[Value]]特征屬性:即屬性的值;
[[Writable]]特征屬性:值為布朗類型,決定該屬性值是否可以寫入;
而只有訪問器屬性有以下兩個屬性:
[[Get]]特征屬性:即為getter函數(shù)內(nèi)容;
[[Set]]特征屬性:即為setter函數(shù)內(nèi)容;
讓我們先來看看這些特征屬性的意義,再來談談如何配置這些特征屬性:
[[Enumerable]]并不是所有的屬性都是可枚舉的,實際上,對象的大部分原生方法的[[Enumerable]]特征屬性的值都被設(shè)置為false(所以使用for-in循環(huán)時,不會遍歷出一大堆你不需要的內(nèi)容),那我們該如何判斷一個屬性是否是可枚舉的呢?
JavaScript為我們提供了.propertyIsEnumerable()方法去檢查一個屬性是否可枚舉,像.hasOwnProperty()方法一樣,每個對象都擁有這個方法:
let obj = { x: 1, } console.log(obj.propertyIsEnumerable("x")) // true[[Configurable]]
可配置是指:
刪除操作;
屬性類型變更操作(從數(shù)據(jù)屬性變?yōu)樵L問器屬性,或者相反):
使用Object.defineProperty()方法配置屬性(別著急,我們之后會著重講解這個方法);
因此,當你設(shè)置某個屬性的[[Configurable]]特征屬性為false時,以上三種操作就都不能正確執(zhí)行。
配置特征屬性是時候講解JavaScript為我們提供的配置屬性特征屬性的方法了:Object.defineProperty()
該方法接收三個參數(shù):
擁有該屬性的對象
屬性名(字符串)
包含需要設(shè)置的特征的屬性描述對象(屬性描述對象具有和特征屬性額同名的屬性,但是名字中不包含中括號)
讓我們看看該方法的實際用法:
let obj = { x: 1, } Object.defineProperty(obj, "x", { enumerable: false, }) console.log("x" in obj) // true console.log(obj.propertyIsEnumerable("x")) // false
我們通過Object.defineProperty()方法使obj對象的x屬性為不可遍歷的,在之后的檢測中,我們看到控制臺輸入屬性存在,但不可遍歷。
讓我們再看看數(shù)據(jù)屬性配置特征屬性的示例:
let obj = { x: 1, } Object.defineProperty(obj, "x", { value: 2, enumerable: true, configurable: true, writable: true, }) console.log(obj.x) // 2 // 注意我們所做的實際上完全等同于以下這段代碼 let obj = { x: 2, }
下面是訪問器屬性配置特征屬性的示例:
let obj = { x: 1, } Object.defineProperty(obj, "x", { get: function() { console.log("reading...") return 1 }, set: function(value) { console.log("setting...") this.x = value }, enumerable: true, configurable: true, })
使用訪問器屬性特征屬性比使用對象字面形式定義訪問器屬性的優(yōu)勢在于,你可以為已有的對象定義這些屬性。如果你想要用對象字面形式,你只能在創(chuàng)建對象時定義訪問器屬性。
需要注意的是,一旦你決定使用Object.defineProperty()方法配置屬性的特征屬性,你需要完整在配置對象中列出enumerable屬性與configurable屬性,因為在默認情況下,這些屬性的值皆為false,這可能不是你想要的。
定義多重屬性當你需要配置一個對象的多個屬性時,你需要使用Object.defineProperties()方法,其用法如下:
let obj = { x: 1, } Object.defineProperties(obj, { x: { value: 2, enumerable: true, configurable: true, writable: true, }, y: { get: function() { console.log("reading...") return 1 }, set: function(value) { console.log("setting...") this.x = value }, }, })獲取屬性特征屬性
目前為止,我們提到了屬性的所有特征屬性,以及如何設(shè)置,最后,讓我們看看JavaScript為我們提供的查看屬性特征屬性的方法:Object.getOwnPropertyDescriptor()。其用法如下:
let obj = { x: 1, } const descriptor = Object.getOwnPropertyDescriptor(obj, "x") console.log(descriptor.enumerable) // true console.log(descriptor.configurable) //true console.log(descriptor.writable) // true console.log(descriptor.value) // 1
可以看到,該方法接收兩個參數(shù),一個目標對象以及想要獲取特征屬性的屬性名,該函數(shù)會返回一個特征屬性描述對象,包含屬性特征屬性的所有信息。
難以置信,我們終于講完了屬性的所有特征屬性。看到這里的你也值得為自己鼓掌?
先休息一會吧,然后我們看看最后的一個主題(還記的我們上一章提到的封閉對象嗎?),定義禁止修改的對象。
對象封印與對象凍結(jié) 對象封印對象封印是指,通過使用Object.seal()方法使一個對象不僅不可擴展,其所有的屬性都不可配置,也就是說,對于一個被封印的對象,你不能:
添加新屬性;
刪除屬性或改變屬性類型;
當一個對象被封印時,你只能讀寫它已有的屬性。另外,我們可以通過Object.isSealed()方法檢驗一個對象是否為被封印對象。
代碼如下:
let obj = { x: 1, } console.log(Object.isExtensible(obj)) // true console.log(Object.isSealed(obj)) // false // 封印對象 Object.seal(obj) console.log(Object.isExtensible(obj)) // false console.log(Object.isSealed(obj)) // true obj.y = 2 console.log("y" in obj) // false obj.x = 3 console.log(obj.x) // 3 delete obj.x console.log(obj.x) // 3對象凍結(jié)
讓我們好好想想對象封印都做了些什么,它使我們不能添加屬性,只能對已有的屬性進行讀寫操作,但卻無法改變已有屬性的特征屬性,也無法刪除已有屬性,我們的對象的封閉性已經(jīng)非常強了。
而對象凍結(jié)則更近一步,將對象屬性的操作限制為只讀,它更像是一個對象某一時刻的快照,除了看之外我們不能對它有任何操作。
在JavaScript中,我們使用Object.freeze()凍結(jié)一個對象,并且使用Object.isFrozen()來判斷一個對象是否被凍結(jié)。
終于結(jié)束了,讓我們簡短回顧一下我們在本章中都講了些什么:
首先,我們講到了屬性的分類:
按來源分:私有屬性 和 原型屬性
按作用分:數(shù)據(jù)屬性 和 訪問器屬性
其次,我們談到了屬性的特征屬性:
共有特征屬性:[[Enumerable]],`[[Configurable]]
數(shù)據(jù)屬性特征屬性:[[Value]],[[Writable]]
訪問器屬性特征屬性:[[Set]],[[Get]]
以及:
配置屬性特征屬性的方法:Object.defineProperty()
定義多重屬性特征屬性的方法:Object.defineProperties()
獲取屬性特征屬性的方法:Object.getOwnPropertyDescriptor()
最后,我們介紹了定義更加封閉對象的兩種方式:
對象封印:Object.seal(),Object.isSealed()方法用于檢驗一個對象是否被封印
對象凍結(jié):Object.freeze(),Object.isFrozen()方法用來判斷一個對象是否被凍結(jié)
大功告成!你現(xiàn)在已經(jīng)和我一樣完全了解JavaScript對象了,Good Job?
? Hey!喜歡這篇文章嗎?別忘了在下方? 點贊讓我知道。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/90051.html
摘要:對象與屬性讓我們保持耐心,再梳理一下對象與屬性的關(guān)系對象是屬性的集合,當對象的屬性是函數(shù)時,我們將其稱之為方法。 這篇博文的主要目的是為了填坑,很久之前我發(fā)表了一篇名為關(guān)于JavaScript對象中的一切(一) — 對象屬性的文章,想要談一談JavaScript對象,可那時只是貼了一張關(guān)于這個主題的思維導圖,今天我會針對這一主題進行展開,將JavaScript對象一些平常不太常用的知識...
摘要:請注意是創(chuàng)建一個全局對象的屬性,而不是聲明了一個全局變量。由于變量聲明自帶不可刪除屬性,比較跟,前者是變量聲明,帶不可刪除屬性,因此無法被刪除后者為全局變量的一個屬性,因此可以從全局變量中刪除。下期預告前端面試你所不知道系列偽類和偽元素 寫在開始 又到了一年的伊始,很多人可能因為各種原因想換一份工作,而找工作難免遇到各種各樣頭痛的面試題,于是我打算寫一個系列,關(guān)于面試中最常見或者前端一...
摘要:已經(jīng)逐漸替代被全世界的開發(fā)者廣泛使用。函數(shù)將一個對象轉(zhuǎn)換成文本化的。不能被文本化的屬性會被忽略。和例外情況在數(shù)組中,不可被的元素用填充。自從年雙十一正式上線,累計處理了億錯誤事件,得到了金山軟件等眾多知名用戶的認可。 譯者按: 老司機們,你知道JSON.stringify還有第二個和第三個可選參數(shù)嗎?它們是什么呢? 原文: What you didn’t know about JSO...
前言 console.log 可以是在日常 Web 開發(fā)中最常用的方法了,但是你應該知道 console 比你想象的強。 1.凡人視角 打印字符串 代碼: console.log(I am a 凡人); 打印提示消息 代碼: console.info(Yes, you are a 凡人); 打印警告消息 代碼: console.warn(凡人你居然敢窺視我); 打印錯誤消息 代碼: console...
前言 console.log 可以是在日常 Web 開發(fā)中最常用的方法了,但是你應該知道 console 比你想象的強。 1.凡人視角 打印字符串 代碼: console.log(I am a 凡人); 打印提示消息 代碼: console.info(Yes, you are a 凡人); 打印警告消息 代碼: console.warn(凡人你居然敢窺視我); 打印錯誤消息 代碼: console...
閱讀 2986·2021-11-23 09:51
閱讀 2798·2021-11-11 16:55
閱讀 2907·2021-10-14 09:43
閱讀 1394·2021-09-23 11:22
閱讀 1035·2019-08-30 11:04
閱讀 1663·2019-08-29 11:10
閱讀 956·2019-08-27 10:56
閱讀 3102·2019-08-26 12:01