摘要:盡管的右操作數(shù)是構造函數(shù),但計算過程實際是檢測了對象的繼承關系。通過創(chuàng)建的對象使用構造函數(shù)的屬性作為它們的原型。
JavaScript之對象屬性 Object.create()繼承
ECMAScript 5定義了一個名為Object.create()的方法,它創(chuàng)建一個新對象, 其中第一個參數(shù)是這個對象的原型。Object.create()提供第二個可選參數(shù),用以對對象的屬性進行進一步描述。
Object.create()是一個靜態(tài)函數(shù),而不是提供給某個對象調(diào)用的方法。使用它的方法很簡單,只須傳入所需的原型對象即可:
var o1 = object.create({x:1, y:2}); // o1繼承了屬性x和yinherit()函數(shù)繼承
通過原型繼承創(chuàng)建一個新對象
// inherit() 返回了一個繼承自原型對象p的屬性的新對象 //這里使用ECMAScript 5中的0bject. create()函數(shù)(如果存在的話) //如果不存在0bject . create(),則退化使用其他方法 function inherit(p) { if (p == nul1) throw TypeError(); // p是一個對象,但不能是null if (Object.create) // 如果bject. create()存在 return object.create(p); // 直接使用它 var t = typeof p; // 否則進行進- -步檢測 if (t !== "object" & t !== "function") throw TypeError(); function f() {}; // 定義一個空構造函數(shù) f.prototype = p; // 將其原型屬性設置為p return new f(); // 使用f()創(chuàng)建p的繼承對象 }對象繼承后屬性的創(chuàng)建、訪問和修改 原型鏈:
假設要查詢對象o的屬性x,如果o中不存在x,那么將會繼續(xù)在o的原型對象中查詢屬性x。如果原型對象中也沒有x,但這個原型對象也有原型,那么繼續(xù)在這個原型對象的原型上執(zhí)行查詢,直到找到x或者查找到一個原型是nul1的對象為止。可以看到,對象的原型屬性構成了一個“鏈”,通過這個“鏈”可以實現(xiàn)屬性的繼承。
實例
var o = {}; o.x = 1; var p = inherit(o); p.y = 2; var q = inherit(p); q.y = 3; var s = q.toString(); q.x + q.y // =>3繼承對象的屬性賦值:
假設給對象o的屬性x賦值:
屬性賦值首先會檢查o中是否已有x屬性;
如果o中已有x屬性,則需先判定x屬性是o繼承的屬性還是自有屬性,從而進一步判定屬性x是否為只讀屬性,如果o的原型鏈中存在該屬性但不允許修改則會導致屬性賦值失敗;
如果o中已有屬性x,但這個屬性不是繼承來的,那么這個賦值操作只是簡單改變o的這個已有屬性x的值,賦值成功。
如果o中已有屬性x,但這個屬性是繼承而來的,屬性x允許賦值操作,那么這個賦值操作只改變這個o的屬性x的值,而不會去修改原型鏈,賦值成功。
如果o中不存在屬性x(原型鏈中也沒有已定義的屬性x),那么賦值操作會直接為o創(chuàng)建一個新的屬性x,賦值成功。
總結:屬性賦值要么失敗,要么創(chuàng)建一個屬性,要么在原始對象中設置屬性,不會影響到原型鏈。但有一個例外,如果o繼承自屬性x,而這個屬性是一個具有setter方法的accessor屬性。
屬性訪問錯誤屬性訪問并不總是返回或設置一個值。查詢一個不存在的屬性并不會報錯,如果在對象o自身的屬性或繼承的屬性中均未找到屬性x,屬性訪問表達式o.x返回undefined。
這里假設book對象有屬性"sub-title",而沒有屬性"subtitle"
book.subtitle; // => undefined: 屬性不存在
但是,如果對象不存在,那么試圖查詢這個不存在的對象的屬性就會報錯。null和undefined值都沒有屬性,因此查詢這些值的屬性會報錯,接上例:
//拋出一個類型錯誤異常,undefined沒有l(wèi)ength屬性 var len = book.subtitle.length;
除非確定book和book.subtitle都是(或在行為上)對象,否則不能這樣寫表達式book.subtitle.length,因為這樣會報錯,下面提供了兩種避免出錯的方法:
方法一
//一種冗余但很易懂的方法 var len = undefined; if (book) { if (book. subtitle) len = book.subtitle.length; }
方法二
//一種更簡練的常用方法,獲取subtitle的length屬性或undefined var len = book && book.subtitle && book.subtitle.length;
這里利用了&&操作符的短路特點。
刪除屬性delete運算符可以刪除對象的屬性。它的操作數(shù)應當是一個屬性訪問表達式。讓人感到意外的是,delete 只是斷開屬性和宿主對象的聯(lián)系,而不會去操作屬性中的屬性。
delete book.author; // book不再有屬性author delete book["main title"]; // book 也不再有屬性"main title"
delete運算符只能刪除自有屬性,不能刪除繼承屬性(要刪除繼承屬性必須從定義這個屬性的原型對象.上刪除它,而且這會影響到所有繼承自這個原型的對象)。
舉例:
a = { p: { x: 1 } }; b = a.p; delete a.p; b.x //=>1,執(zhí)行這段代碼之后b.x的值依然是1
由于已經(jīng)刪除的屬性的引用依然存在,因此在JavaScript的某些實現(xiàn)中,可能因為這種不嚴謹?shù)拇a而造成內(nèi)存泄漏。所以在銷毀對象的時候,要遍歷屬性中的屬性,依次刪除。
當delete表達式刪除成功或沒有任何副作用(比如刪除不存在的屬性)時,它返回true。如果delete后不是一個屬性訪問表達式,delete同樣返回true:
delete不能刪除那些可配置性為false的屬性(盡管可以刪除不可擴展對象的可配置屬性)。某些內(nèi)置對象的屬性是不可配置的,比如通過變量聲明和函數(shù)聲明創(chuàng)建的全局對象的屬性。在嚴格模式中,刪除一個不可配置屬性會報一個類型錯誤。
屬性檢測 1. isPrototypeOf()方法檢測一個對象是否是另一個對象的原型。或者說一個對象是否被包含在另一個對象的原型鏈中
實例一:
var p = {x:1}; //定義一個原型對象 var o = Object.create(p); //使用這個原型創(chuàng)建一個對象 p.isPrototypeOf(o); //=>true:o繼承p Object.prototype.isPrototypeOf(p); //=> true, p繼承自Object.prototype
實例二:
function Animal(){ this.species = "動物"; }; var eh = new Animal(); Animal.prototype.isPrototypeOf(eh) //=>true
綜合上面的兩個例子,我們發(fā)現(xiàn)在調(diào)用isPrototypeOf()的時候有三種方式
p.isPrototypeOf(o); //=>true Object.prototype.isPrototypeOf(p); Animal.prototype.isPrototypeOf(eh) //=>true
總結一下就是:
通過Object.create()創(chuàng)建的對象使用第一個參數(shù)作為原型2. instanceof 運算符
通過對象直接量的對象使用Object.prototype作為原型
通過new創(chuàng)建的對象使用構造函數(shù)的prototype屬性作為原型
Instanceof運算符希望左操作數(shù)是一個對象,右操作數(shù)標識對象的類。如果左側對象是右側類的實例,則表達式返回為true,否則返回false。
javascript var d = new Date(); d instanceof Date; //=>true d是Date的實例 d instanceof Object; //=>true 所有對象都是Object的實
當通過instanceof判斷一個對象是否是一個類的實例的時候,這個判斷也會包含對父類的檢測。盡管instanceof的右操作數(shù)是構造函數(shù),但計算過程實際是檢測了對象的繼承關系。
instanceOf與isPrototypeOf簡單總結
var d = new Date(); Date.prototype.isPrototypeOf(d); //=>true d instanceof Date; //=>true
* 如果Date.prototype是d的原型,那么d一定是Date的實例。 * 缺點為無法同對象來獲得類型,只能檢測對象是否屬于類名 * 在多窗口和多框架的子頁面的web應用中兼容性不佳。其中一個典型代表就是instanceof操作符不能視為一個可靠的數(shù)組檢測方法。3. hasOwnProperty()方法
對象的hasOwnProperty()方法用來檢測給定的名字是否是對象的自有屬性。對于繼承屬性它將返回false:
var o = { x: 1 } o.hasOwnProperty("x"); // true: o有一個自有屬性x o.hasOwnProperty("y"); // false: o中不存在屬性y o.hasOwnProperty("toString"); // false: toString是繼承屬性4. in操作符
in運算符左側是屬性名,右側是對象,如果對象的自有屬性或者繼承屬性中包含這個屬性則返回true。
var o = { x = 1 } "x" in o; // =>true "y" in o; // =>false "toString" in o; // =>true: toString是繼承屬性5. propertyIsEnumberable()方法
只有檢測到是自有屬性且這個屬性可枚舉(enumberable attribute)為true時它才返回true。某些內(nèi)置屬性是不能枚舉的。
var o = inherit({ y: 2 }); o.x = 1; o.propertyIsEnumerable("x"); // true: o有一個可枚舉的自有屬性x o.propertyIsEnumerable("y"); // false: y是繼承來的 Object.prototype.propertyIsEnumerable("toString"); // false: 不可枚舉枚舉屬性 1. for/in循環(huán)
可以在循環(huán)體中遍歷對象中所有可枚舉的屬性(包括自有屬性和繼承的屬性),把屬性名稱賦值給循環(huán)變量。對象繼承的內(nèi)置方法不可枚舉的,但在代碼中給對象添加的屬性都是可枚舉的(除非用下文中提到的一個方法將它們轉換為不可枚舉的)。例如:
var o = {x:1, y:2, z:3}; //三個可枚舉的自有屬性 o.propertyIsEnumerable("toString") // =>false, 不可枚舉 for(p in o) //遍歷屬性 console.log(p); //輸出x、y和z,不會輸出toString
有許多實用工具庫給0bject.prototype添加了新的方法或屬性,這些方法和屬性可以被所有對象繼承并使用。然而在ECMAScript 5標準之前,這些新添加的方法是不能定義為不可枚舉的,因此它們都可以在for/i n循環(huán)中枚舉出來。為了避免這種情況,需要過濾for/in循環(huán)返回的屬性,下面兩種方式是最常見的:
for(p in o) { if (!o. hasOwnProperty(p)) continue; // 跳過繼承的屬性 } for(p in o) { if (typeof o[p] === "function") continue; // 跳過方法 }2. Object.getOwnPropertyNames()方法
返回一個由指定對象的所有自身屬性的屬性名(包括不可枚舉屬性但不包括Symbol值作為名稱的屬性)組成的數(shù)組。
var arr = ["a", "b", "c"]; console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"] // 類數(shù)組對象 var obj = { 0: "a", 1: "b", 2: "c"}; console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"] // 使用Array.forEach輸出屬性名和屬性值 Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) { console.log(val + " -> " + obj[val]); }); // 輸出 // 0 -> a // 1 -> b // 2 -> c //不可枚舉屬性 var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; }, enumerable: false } }); my_obj.foo = 1; console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]3. Object.keys()方法
返回一個由一個給定對象的自身可枚舉屬性組成的數(shù)組,數(shù)組中屬性名的排列順序和使用for…in循環(huán)遍歷該對象時返回的順序一致 。如果對象的鍵-gs值都不可枚舉,那么將返回由鍵組成的數(shù)組。
// simple array var arr = ["a", "b", "c"]; console.log(Object.keys(arr)); // console: ["0", "1", "2"] // array like object var obj = { 0: "a", 1: "b", 2: "c" }; console.log(Object.keys(obj)); // console: ["0", "1", "2"] // array like object with random key ordering var anObj = { 100: "a", 2: "b", 7: "c" }; console.log(Object.keys(anObj)); // console: ["2", "7", "100"] // getFoo is a property which isn"t enumerable var myObj = Object.create({}, { getFoo: { value: function () { return this.foo; } } }); myObj.foo = 1; console.log(Object.keys(myObj)); // console: ["foo"]屬性的特性
我們將存取器屬性的getter和setter方法看成是屬性的特性。按照這個邏輯, 我們也可以把數(shù)據(jù)屬性的值同樣看做屬性的特性。因此,可以認為一個屬性包含一個名字和4個特性。
數(shù)據(jù)屬性的4個特性分別是它的值(value) 、可寫性(writable) 、可枚舉性(enumerable) 和可配置性(configurable) 。
存取器屬性不具有值(value) 特性和可寫性,它們的可寫性是由setter方法存在與否決定的。因此存取器屬性的4個特性是讀取(get)、寫入(set)、可枚舉性和可配置性。
為了實現(xiàn)屬性特性的查詢和設置操作,ECMAScript 5中定義了一個名為“屬性描述符”(property descriptor)的對象,這個對象代表那4個特性。描述符對象的屬性和它們所描述的屬性特性是同名的。
因此,數(shù)據(jù)屬性的描述符對象的屬性有value、writable.enumerable和configurable。存取器屬性的描述符對象則用get屬性和set屬性代替value和writable。其中writable、 enumerable和configurable都是布爾值,當然,get屬性和set屬性是函數(shù)值。
對象的三個屬性每一個對象都有與之相關的原型(prototype) 、類(class) 和可擴展性(extensible attribute)。
原型屬性
對象的原型屬性是用來繼承屬性的,這個屬性如此重要,以至于我們經(jīng)常把“o的原型屬性”直接叫做“o的原型”。
原型屬性是在實例對象創(chuàng)建之初就設置好的,通過對象直接量創(chuàng)建的對象使用Object. prototype作為它們的原型。通過new創(chuàng)建的對象使用構造函數(shù)的prototype屬性作為它們的原型。通過Object.create() 創(chuàng)建的對象使用第一-個參數(shù)(也可以是null)作為它們的原型。
類屬性
對象的類屬性(class attribute) 是-一個字符串,用以表示對象的類型信息。ECMAScript3和ECMAScript 5都未提供設置這個屬性的方法,并只有一種間接的方法可以查詢它。默認的toString()方法(繼承自Object.prototype)返回了如下這種格式的字符串:[object class]
因此,要想獲得對象的類,可以調(diào)用對象的toString()方法,然后提取已返回字符串的第8個到倒數(shù)第二個位置之間的字符。
可拓展性
對象的可擴展性用以表示是否可以給對象添加新屬性。所有內(nèi)置對象和自定義對象都是顯式可擴展的,宿主對象的可擴展性是由JavaScript引擎定義的。在ECMAScript 5中,所有的內(nèi)置對象和自定義對象都是可擴展的,除非將它們轉換為不可擴展的,同樣,宿主對象的可擴展性也是由實現(xiàn)ECMAScript 5的JavaScript引擎定義的。
對象序列化(serialization) 是指將對象的狀態(tài)轉換為字符串,也可將字符串還原為對象。ECMAScript 5提供了內(nèi)置函數(shù)JSON.stringify()和JSON.parse()用來序列化和還原JavaScript對象。這些方法都使用JSON作為數(shù)據(jù)交換格式,JSON的全稱是“JavaScript Object Notation" 一JavaScript對象表示法,它的語法和JavaScript對象與數(shù)組直接量的語法非常相近:
o = {x:1, y:{z:[false, null, ""]}}; //定義一個測試對象 s = JSON.stringify(o); // s是"{"x":1,"y":{"z" :[false, null, ""]}}" p = JSON.parse(s); // p是o的深拷貝
參考:
* 《JavaScript權威指南》第六版 * [MDN Web 文檔](https://developer.mozilla.org/zh-CN/)
推薦閱讀:
【專題:JavaScript進階之路】
JavaScript之“use strict”
JavaScript之new運算符
JavaScript之call()理解
我是Cloudy,年輕的前端攻城獅一枚,愛專研,愛技術,愛分享。
個人筆記,整理不易,感謝閱讀、點贊和收藏。
文章有任何問題歡迎大家指出,也歡迎大家一起交流前端各種問題!
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105326.html
摘要:對象數(shù)組初始化表達式,闖關記之上文檔對象模型是針對和文檔的一個。闖關記之數(shù)組數(shù)組是值的有序集合。數(shù)組是動態(tài)的,根闖關記之語法的語法大量借鑒了及其他類語言如和的語法。 《JavaScript 闖關記》之 DOM(下) Element 類型 除了 Document 類型之外,Element 類型就要算是 Web 編程中最常用的類型了。Element 類型用于表現(xiàn) XML 或 HTML 元素...
摘要:深入系列第四篇,具體講解執(zhí)行上下文中的變量對象與活動對象。下一篇文章深入之作用域鏈本文相關鏈接深入之執(zhí)行上下文棧深入系列深入系列目錄地址。 JavaScript深入系列第四篇,具體講解執(zhí)行上下文中的變量對象與活動對象。全局上下文下的變量對象是什么?函數(shù)上下文下的活動對象是如何分析和執(zhí)行的?還有兩個思考題幫你加深印象,快來看看吧! 前言 在上篇《JavaScript深入之執(zhí)行上下文棧》中...
摘要:深入系列第七篇,結合之前所講的四篇文章,以權威指南的為例,具體講解當函數(shù)執(zhí)行的時候,執(zhí)行上下文棧變量對象作用域鏈是如何變化的。前言在深入之執(zhí)行上下文棧中講到,當代碼執(zhí)行一段可執(zhí)行代碼時,會創(chuàng)建對應的執(zhí)行上下文。 JavaScript深入系列第七篇,結合之前所講的四篇文章,以權威指南的demo為例,具體講解當函數(shù)執(zhí)行的時候,執(zhí)行上下文棧、變量對象、作用域鏈是如何變化的。 前言 在《Jav...
摘要:返回值是一個對象,如果是訪問器屬性,這個對象的屬性有和如果是數(shù)據(jù)屬性,這個對象的屬性有和。上一篇面向對象版塊之對象屬性下一篇面向對象版塊之創(chuàng)建對象 這是 javascript 面向對象版塊的第三篇文章,主要講解的是多個屬性的定義以及讀取屬性的特性。前面這幾章內(nèi)容目的在于加深對對象的理解,這樣可以利于理解后面的原型鏈以及繼承方面的知識,或者你也可以了解一下不一樣的 javascript ...
摘要:下面,讓我們以一個函數(shù)的創(chuàng)建和激活兩個時期來講解作用域鏈是如何創(chuàng)建和變化的。這時候執(zhí)行上下文的作用域鏈,我們命名為至此,作用域鏈創(chuàng)建完畢。 JavaScript深入系列第五篇,講述作用鏈的創(chuàng)建過程,最后結合著變量對象,執(zhí)行上下文棧,讓我們一起捋一捋函數(shù)創(chuàng)建和執(zhí)行的過程中到底發(fā)生了什么? 前言 在《JavaScript深入之執(zhí)行上下文棧》中講到,當JavaScript代碼執(zhí)行一段可執(zhí)行代...
摘要:深入系列的第一篇,從原型與原型鏈開始講起,如果你想知道構造函數(shù)的實例的原型,原型的原型,原型的原型的原型是什么,就來看看這篇文章吧。讓我們用一張圖表示構造函數(shù)和實例原型之間的關系在這張圖中我們用表示實例原型。 JavaScript深入系列的第一篇,從原型與原型鏈開始講起,如果你想知道構造函數(shù)的實例的原型,原型的原型,原型的原型的原型是什么,就來看看這篇文章吧。 構造函數(shù)創(chuàng)建對象 我們先...
閱讀 3350·2021-11-04 16:10
閱讀 3846·2021-09-29 09:43
閱讀 2692·2021-09-24 10:24
閱讀 3338·2021-09-01 10:46
閱讀 2503·2019-08-30 15:54
閱讀 585·2019-08-30 13:19
閱讀 3232·2019-08-29 17:19
閱讀 1049·2019-08-29 16:40