您好,登錄后才能下訂單哦!
本篇文章展示了JavaScript中的對象屬性和應用,代碼簡明扼要容易理解,絕對能讓你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
JavaScript 中的對象概念的確很容易讓人困惑。看下面一個例子:
var strPrimitive = "I'm mamacat"; typeof strPrimitive; // "string" strPrimitive instanceof String; // false var strObject = new String("I'm mamacat"); typeof strObject; // "object" strObject instanceof String; // true strPrimitive.substr(8, 3); // "cat"
同樣的字符串賦值到對象,一會兒是字符串類型一會兒是對象,而明明不是對象類型的變量還是可以使用對象屬性,為什么會這樣呢?
類型和內置對象
JavaScript 中一共有六種主要(語言)類型,即 string, number, boolean, null, undefined 和 object,其中前五個基本類型都不是對象(對 null 進行 typeof 得到的是 "object",這是語言本身的 BUG)。而在此之外,則有許多特殊的對象子類型,例如數組、函數和內置對象等。
有些內置對象的名字看著和簡單基本類型一樣,就比如 String,Boolean,Object 之類。這些內置對象從表現形式看就和別的面向對象語言中的“類”概念差不多,而正如上篇文章所屬,它們實際使只是一些能被用來構造一個對應子類型的內置函數而已(不要困惑,函數也是對象,這里并不矛盾)。于是就可以回到最初的例子,strObject 是由內置函數/內置對象 String 所構造的變量,對應 String 子類型,所以它是一個對象,而 strPrimitive 則是一個原始字面值而已。
當然,上面例子中最下面我們看上去對 strPrimitive 調用了 substr() 函數,這里則是因為,JavaScript 引擎會在需要時,把原始字面量轉換成對應的對象,而轉換之后我們自然就可以使用屬性訪問對應的方法了。
對象屬性
那么,就上方的例子而言,String 對象實例就會有 substr() 函數可以用,但根據之前的文章可以知道,這些“函數”本身并不屬于某個對象,而這些函數實質是對應對象的一個屬性。當然,即便我們說某種類型的對象本身具備各種屬性,實際上這些屬性也多是各自獨立存在的,只不過以引用的形式關聯在了一起而已,這和之前了解的內容也并不矛盾。這些被關聯起來的東西,被稱為對象的 屬性。
對象的復制
插播一條快報,盡管之前的文章提到過,上方也又一次反復強調過屬性只是以引用的形式關聯起來的獨立存在,我們有時仍然會“理所應當”的認為屬性是對象的一部分,而最容易因此踩坑的地方之一就是對象的復制了。仔細思考即可知道,當我們復制對象時,由于其屬性本身只是引用關聯,故“復制”得到的對象所包含的屬性引用指向的和原本對象的屬性引用其實還是同一個位置:
var ori = { a : 1}; var ori_copy = ori; ori.a = 61; ori_copy.a; // 61
顯然這很可能和我們的期望不一樣,而我們想要真正的拷貝對象則沒有完美適用性的方案,很多時候的常規做法則是把對象序列化一下,然后再以此反序列化得到新的對象來實現對象的拷貝(比如使用 json)。ES6 中新增了 Object.assign()
來進行對象的淺拷貝,做法是把對象的所有可枚舉屬性等號賦值到新對象中。不過仍需注意的是,等號賦值并不會賦值屬性的元信息(屬性描述符,后述),在需要的情況下應當特別留意。
屬性訪問和數組
訪問對象所關聯的屬性的方式即通過 . 或者 [] 操作符進行訪問,obj.a 和 obj["a"] 訪問的屬性實質上是一樣的,而這兩種訪問形式的區別也只有訪問的屬性名稱里能不能有奇怪的符號而已。[] 操作符內扔的是個字符串,實際上屬性名也永遠都是字符串。當然,這個概念可能比較意外的就是,數組的下標訪問其實并不是例外,數字還是被轉換成了字符串才被使用的。
// 對象的屬性訪問: var tejilang = {1 : "Teji Wolf"}; tejilang instanceof Array; // false tejilang["1"]; // "Teji Wolf" tejilang[1]; // "Teji Wolf" // 這回保證它是 Array var macat = ["codingcat"]; macat instanceof Array; // true macat.length; // 1 macat[0]; // "codingcat" macat["0"]; // "codingcat" macat.length = 20; macat; // (20) ["codingcat", empty × 19]
數組下標既然不屬例外情況,那數組對象必然有其它屬性控制數組本身的行為,例如上例中,macat 數組的長度就是 length 屬性所體現的,通過修改它的值也就改變了對象本身對外的表現形式。當然,由于數組本身就是對象,所以我們還是可以把數組當鍵值對來用,只是這種做法通常是沒有意義且會讓人感到困惑的。JavaScript 引擎通常都根據對象的類型做了不同程度的優化,故除了代碼邏輯可讀性外,合理的使用也是多少可以改善性能的。
能夠通過字符訪問屬性還是存在一些別的好處的,比如 ES6 的可計算屬性名。當然 ES6 不在本文的關注范圍內,所以這里就不再討論了。
屬性描述符
有時我們可能不希望某個屬性被隨意修改,有時候我們需要額外配置一些屬性的信息,自 ES5 起,所有的屬性就都具備了“屬性描述符”(Property Descriptor)來控制屬性本身的這些元信息。
數據描述符
來看這個例子:
var chris = {}; Object.defineProperty(chris, "IQ", { value: 228, writable: false, configurable: true, enumerable: true }); chris.IQ = 61; // 靜默失敗了,如果是嚴格模式則會 TypeError chris.IQ; // 228
通過 defineProperty 可以對一個對象的屬性配置其對應的屬性描述符(元信息),而屬性描述符則包含訪問描述符和數據描述符,上面的例子中,defineProperty 的第三個參數就定義了數據的若干數據描述符,其中 writable 表示可寫,configurable 表示屬性是否可配置(注意,修改成不可配置是單向操作),enumerable 則表示屬性是否應當出現在枚舉中,比如 for..in 中。
顯然我們可以通過屬性描述符實現對屬性的保護,而同時也存在一些方便函數來做近似的事。如 Object.preventExtensions() 會保留原有屬性但禁止添加新屬性,Object.seal() 會密封對象,在禁止添加新屬性的基礎上把原有屬性標記為不可配置,Object.freeze() 會凍結對象,即在密封的基礎上把數據訪問屬性標記為不可寫。
[[Get]], [[Put]] 和訪問描述符
在我們訪問和賦值一個對象的屬性時,實際上是通過 [[Get]] 和 [[Put]] 操作進行的,例如屬性訪問時,[[Get]] 會先找有沒有這個屬性,如果沒有則會遍歷對象的 [[Prototype]] 鏈(原型鏈,這次不談這個概念)來找,實在找不到則返回 undefined 。而這個行為實際是允許我們通過設置 getter (get())和 setter (set())函數來改變的,它們被稱為 訪問描述符。
當我們提供訪問描述符時,對應的訪問操作就不再受到 value 和 writable 屬性的影響了,另外需要注意的是,盡管它們也是屬性描述符,但定義 getter 和 setter 并不要求一定要通過 defineProperty 設置:
var obj = { get a() { // 給 a 屬性定義 getter return this._a_; }, set a(val) { // a 屬性的 setter this._a_ = val * 2; } } obj.a = 2; obj.a; // 4
屬性存在性
因為屬性的值也可能是 undefined,不存在的屬性直接訪問得到的也是 undefined,所以直接通過簡單的屬性訪問是無法區分是否存在的,這時我們即可通過 in 或者 hasOwnProperty() 檢查屬性是否存在對象中了:
var obj = {a : 2}; "a" in obj; // true obj.hasOwnProperty("a"); // true
盡管仍沒有講到原型鏈的概念,這里仍然應注意,in 操作符會檢查原型鏈中是否存在屬性,而 hasOwnProperty 則不會。另外在一些情況下,有的對象會沒有 hasOwnProperty 這個屬性(此處不提原因),這時可以用過 Object.prototype.hasOwnProperty.call(objName, propertyName) 來實現檢查。
看完上述內容,你們對JavaScript中的對象屬性和應用的更了解了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。