與對(duì)象和屬性一起工作

與對(duì)象和屬性一起工作

復(fù)雜對(duì)象可以保存任何允許的 JavaScript 值。在以下代碼中,我創(chuàng)建一個(gè)名為 myObject 的 Object() 對(duì)象,然后添加表示 JavaScript 中可用的大多數(shù)值的屬性。


復(fù)雜對(duì)象

示例:sample29.html

 <script>  	var myObject = {};  	// Contain properties inside of myObject representing most of the native JavaScript values. 	myObject.myfunction = function () { }; 	myObject.myArray = []; 	myObject.myString = 'string'; 	myObject.mynumber = 33; 	myObject.myDate = new Date(); 	myObject.myRegExp = /a/; 	myObject.myNULL = null; 	myObject.myundefined = undefined; 	myObject.myObject = {}; 	myObject.myMath_PI = Math.PI; 	myObject.myError = new Error('Darn!');  	console.log(myObject.myFunction, myObject.myArray, myObject.myString, myObject.myNumber, myObject.myDate, myObject.myRegExp, myObject.myNull, myObject.myNull, myObject.myUndefined, myObject.myObject, myObject.myMath_PI, myObject.myError);  	/* Works the same with any of the complex objects, for example a function. */ 	var myFunction = function () { };  	myFunction.myFunction = function () { }; 	myFunction.myArray = []; 	myFunction.myString = 'string'; 	myFunction.myNumber = 33; 	myFunction.myDate = new Date(); 	myFunction.myRegExp = /a/; 	myFunction.myNull = null; 	myFunction.myUndefined = undefined; 	myFunction.myObject = {}; 	myFunction.myMath_PI = Math.PI; 	myFunction.myError = new Error('Darn!');  	console.log(myFunction.myFunction, myFunction.myArray, myFunction.myString, myFunction.myNumber, myFunction.myDate, myFunction.myRegExp, myFunction.myNull, myFunction.myNull, myFunction.myUndefined, myFunction.myObject, myFunction.myMath_PI, myFunction.myError);  </script>

這里要學(xué)習(xí)的簡(jiǎn)單概念是,復(fù)雜對(duì)象可以包含任何可以在 JavaScript 中名義上表達(dá)的內(nèi)容。當(dāng)您看到此操作完成時(shí),您不應(yīng)該感到驚訝,因?yàn)樗斜緳C(jī)對(duì)象都可以發(fā)生變化。這甚至適用于對(duì)象形式的 String()、Number() 和 Boolean() 值,即使用 new 運(yùn)算符創(chuàng)建它們時(shí)。


以編程方式有益的方式封裝復(fù)雜對(duì)象

Object()、Array() 和 Function() 對(duì)象可以包含其他復(fù)雜對(duì)象。在下面的示例中,我通過(guò)使用 Object() 對(duì)象設(shè)置對(duì)象樹(shù)來(lái)演示這一點(diǎn)。

示例:sample30.html

 <script>  // Encapsulation using objects creates object chains. var object1 = { 	object1_1: { 		object1_1_1: {foo: 'bar'},  		object1_1_2: {},  	},  	object1_2: { 		object1_2_1: {},  		object1_2_2: {},  	} };  console.log(object1.object1_1.object1_1_1.foo); // Logs 'bar'.  </script>

可以使用 Array() 對(duì)象(又名多維數(shù)組)或 Function() 對(duì)象完成同樣的操作。

示例:sample31.html

 <script>  	// Encapsulation using arrays creates a multidimensional array chain. 	var myArray = [[[]]]; // An empty array, inside an empty array, inside an empty array.  	/* Here is an example of encapsulation using functions: An empty function inside an empty function inside an empty function. */ 	var myFunction = function () { 		// Empty function. 		var myFunction = function () { 			// Empty function. 			var myFunction = function () { 				// Empty function. 			}; 		}; 	};  	// We can get crazy and mix and match too. 	var foo = [{ foo: [{ bar: { say: function () { return 'hi'; } }}]}]; 	console.log(foo[0].foo[0].bar.say()); // Logs 'hi'.  </script>

這里要掌握的主要概念是,一些復(fù)雜對(duì)象被設(shè)計(jì)為以編程上有益的方式封裝其他對(duì)象。


使用點(diǎn)表示法或括號(hào)表示法獲取、設(shè)置和更新對(duì)象的屬性

我們可以使用點(diǎn)符號(hào)或方括號(hào)符號(hào)來(lái)獲取、設(shè)置或更新對(duì)象的屬性。

在下面的示例中,我演示了點(diǎn)表示法,這是通過(guò)使用對(duì)象名稱(chēng)后跟句點(diǎn),然后后跟要獲取、設(shè)置或更新的屬性來(lái)完成的(例如,objectName.Property)。

示例:sample32.html

 <script>  	// Create a cody Object() object. 	var cody = new Object();  	// Setting properties. 	cody.living = true; 	cody.age = 33; 	cody.gender = 'male'; 	cody.getGender = function () { return cody.gender; };  	// Getting properties. 	console.log( 		cody.living, 		cody.age, 		cody.gender, 		cody.getGender() 		); // Logs 'true 33 male male'.  	// Updating properties, exactly like setting. 	cody.living = false; 	cody.age = 99; 	cody.gender = 'female'; 	cody.getGender = function () { return 'Gender = ' + cody.gender; };  	console.log(cody);  </script>

點(diǎn)表示法是獲取、設(shè)置或更新對(duì)象屬性的最常見(jiàn)表示法。

除非需要,否則括號(hào)表示法并不常用。在下面的示例中,我將上一個(gè)示例中使用的點(diǎn)符號(hào)替換為括號(hào)符號(hào)。對(duì)象名稱(chēng)后跟一個(gè)左括號(hào)、屬性名稱(chēng)(用引號(hào)引起來(lái)),然后是一個(gè)右括號(hào):

示例:sample33.html

 <script>  	// Creating a cody Object() object. 	var cody = new Object();  	// Setting properties. 	cody['living'] = true; 	cody['age'] = 33; 	cody['gender'] = 'male'; 	cody['getGender'] = function () { return cody.gender; };  	// Getting properties. 	console.log( 		cody['living'], 		cody['age'], 		cody['gender'], 		cody['getGender']() // Just slap the function invocation on the end! 		); // Logs 'true 33 male male'.  	// Updating properties, very similar to setting. 	cody['living'] = false; 	cody['age'] = 99; 	cody['gender'] = 'female'; 	cody['getGender'] = function () { return 'Gender = ' + cody.gender; };  	console.log(cody);  </script>

當(dāng)您需要訪問(wèn)屬性鍵并且您必須使用包含表示屬性名稱(chēng)的字符串值的變量時(shí),括號(hào)表示法非常有用。在下一個(gè)示例中,我通過(guò)使用方括號(hào)表示法訪問(wèn)屬性 foobar 來(lái)演示括號(hào)表示法相對(duì)于點(diǎn)表示法的優(yōu)勢(shì)。我使用兩個(gè)變量來(lái)執(zhí)行此操作,這兩個(gè)變量在連接時(shí)會(huì)生成 foobarObject 中包含的屬性鍵的字符串版本。

示例:sample34.html

 <script>  	var foobarObject = { foobar: 'Foobar is code for no code' };  	var string1 = 'foo'; 	var string2 = 'bar';  	console.log(foobarObject[string1 + string2]); // Let's see dot notation do this!  </script>

此外,括號(hào)表示法可以方便地獲取無(wú)效 JavaScript 標(biāo)識(shí)符的屬性名稱(chēng)。在下面的代碼中,我使用一個(gè)數(shù)字和一個(gè)保留關(guān)鍵字作為屬性名稱(chēng)(作為字符串有效),只有括號(hào)表示法才能訪問(wèn)該屬性名稱(chēng)。

示例:sample35.html

 <script>  	var myObject = { '123': 'zero', 'class': 'foo' };  	// Let's see dot notation do this! Keep in mind 'class' is a keyword in JavaScript. 	console.log(myObject['123'], myObject['class']); //Logs 'zero foo'.  	// It can't do what bracket notation can do, in fact it causes an error. 	// console.log(myObject.0, myObject.class);  </script>

因?yàn)閷?duì)象可以包含其他對(duì)象,所以 cody.object.object.object.object 或 cody[‘object’][‘object’][‘object’][‘object’] 可以在以下位置查看次。這稱(chēng)為對(duì)象鏈。對(duì)象的封裝可以無(wú)限期地進(jìn)行下去。

對(duì)象在 JavaScript 中是可變的,這意味著可以隨時(shí)對(duì)大多數(shù)對(duì)象執(zhí)行獲取、設(shè)置或更新它們。通過(guò)使用括號(hào)表示法(例如,cody[‘age’]),您可以模仿其他語(yǔ)言中的關(guān)聯(lián)數(shù)組

如果對(duì)象內(nèi)的屬性是方法,您所要做的就是使用 () 運(yùn)算符(例如 cody.getGender())來(lái)調(diào)用屬性方法。


刪除對(duì)象屬性

delete 運(yùn)算符可用于完全刪除對(duì)象的屬性。在下面的代碼片段中,我們從 foo 對(duì)象中刪除 bar 屬性。

示例:sample36.html

 <script>  	var foo = { bar: 'bar' }; 	delete foo.bar; 	console.log('bar' in foo); // Logs false, because bar was deleted from foo.  </script>

delete 不會(huì)刪除在原型鏈上找到的屬性。

刪除是實(shí)際從對(duì)象中刪除屬性的唯一方法。將屬性設(shè)置為 undefined 或 null 僅更改該屬性的值。它不會(huì)從對(duì)象中刪除屬性。


如何解析對(duì)對(duì)象屬性的引用

如果您嘗試訪問(wèn)對(duì)象中未包含的屬性,JavaScript 將嘗試使用原型鏈查找該屬性或方法。在下面的示例中,我創(chuàng)建一個(gè)數(shù)組并嘗試訪問(wèn)尚未定義的名為 foo 的屬性。您可能會(huì)認(rèn)為,由于 myArray.foo 不是 myArray 對(duì)象的屬性,JavaScript 將立即返回 undefined。但是 JavaScript 會(huì)在另外兩個(gè)地方(Array.prototype 和 Object.prototype)查找 foo 的值,然后返回 undefined。

示例:sample37.html

 <script>  	var myArray = [];  	console.log(myArray.foo); // Logs undefined.  	/* JS will look at Array.prototype for Array.prototype.foo, but it is not there. Then it will look for it at Object.prototype, but it is not there either, so undefined is returned! */  </script>

財(cái)產(chǎn)。如果它有該屬性,它將返回該屬性的值,并且不會(huì)發(fā)生繼承,因?yàn)樵玩湜](méi)有被杠桿化。如果實(shí)例沒(méi)有該屬性,JavaScript 將在對(duì)象的構(gòu)造函數(shù) prototype 對(duì)象中查找它。

所有對(duì)象實(shí)例都有一個(gè)屬性,該屬性是創(chuàng)建該實(shí)例的構(gòu)造函數(shù)的秘密鏈接(又名 __proto__)。可以利用這個(gè)秘密鏈接來(lái)獲取構(gòu)造函數(shù),特別是實(shí)例構(gòu)造函數(shù)的原型屬性。

這是 JavaScript 中對(duì)象最令人困惑的方面之一。但讓我們來(lái)推理一下。請(qǐng)記住,函數(shù)也是具有屬性的對(duì)象。允許對(duì)象從其他對(duì)象繼承屬性是有意義的。就像說(shuō):“嘿,對(duì)象 B,我希望你分享對(duì)象 A 擁有的所有屬性。”默認(rèn)情況下,JavaScript 通過(guò) prototype 對(duì)象將這一切連接到本機(jī)對(duì)象。當(dāng)您創(chuàng)建自己的構(gòu)造函數(shù)時(shí),您也可以利用原型鏈。

JavaScript 到底是如何實(shí)現(xiàn)這一點(diǎn)的?在您了解它的本質(zhì)之前,您會(huì)感到困惑:只是一組規(guī)則。讓我們創(chuàng)建一個(gè)數(shù)組來(lái)更仔細(xì)地檢查 prototype 屬性。

示例:sample38.html

 <script>  	// myArray is an Array object. 	var myArray = ['foo', 'bar'];  	console.log(myArray.join()); // join() is actually defined at Array.prototype.join  </script>

我們的 Array() 實(shí)例是一個(gè)具有屬性和方法的對(duì)象。當(dāng)我們?cè)L問(wèn)其中一種數(shù)組方法時(shí),例如 join(),我們問(wèn)自己:從 Array() 構(gòu)造函數(shù)創(chuàng)建的 myArray 實(shí)例是否有自己的 join() 方法?我們來(lái)檢查一下。

示例:sample39.html

 <script>  	var myArray = ['foo', 'bar'];  	console.log(myArray.hasOwnProperty('join')); // Logs false.  </script>

不,沒(méi)有。然而 myArray 可以訪問(wèn) join() 方法,就好像它是它自己的屬性一樣。這里發(fā)生了什么?好吧,您剛剛觀察了原型鏈的運(yùn)行情況。我們?cè)L問(wèn)了一個(gè)屬性,盡管該屬性不包含在 myArray 對(duì)象中,但 JavaScript 可以在其他地方找到該屬性。其他地方是非常具體的。當(dāng) Array() 構(gòu)造函數(shù)由 JavaScript 創(chuàng)建時(shí),join() 方法被添加(除其他外)作為 Array() 的 prototype 屬性的屬性。

重申一下,如果您嘗試訪問(wèn)不包含該屬性的對(duì)象上的屬性,JavaScript 將在 prototype 鏈中搜索該值。首先,它將查看創(chuàng)建對(duì)象的構(gòu)造函數(shù)(例如,Array),并檢查其原型(例如,Array.prototype)以查看是否可以在那里找到該屬性。如果第一個(gè)原型對(duì)象沒(méi)有該屬性,則 JavaScript 會(huì)繼續(xù)在初始構(gòu)造函數(shù)后面的構(gòu)造函數(shù)中沿鏈向上搜索。它可以一直做到這一點(diǎn),直到鏈的末端。

鏈條的終點(diǎn)在哪里?讓我們?cè)俅螜z查該示例,在 myArray 上調(diào)用 toLocaleString() 方法。

示例:sample40.html

 <script>  	// myArray and Array.prototype contain no toLocaleString() method. 	var myArray = ['foo', 'bar'];  	// toLocaleString() is actually defined at Object.prototype.toLocaleString 	console.log(myArray.toLocaleString()); // Logs 'foo,bar'.  </script>

toLocaleString() 方法未在 myArray 對(duì)象中定義。因此,原型鏈接規(guī)則被調(diào)用,JavaScript 在 Array 構(gòu)造函數(shù)原型屬性中查找屬性(例如,Array.prototype)。它也不存在,因此再次調(diào)用鏈?zhǔn)揭?guī)則,我們?cè)?Object() 原型屬性 (Object.prototype) 中查找該屬性。是的,它在那里找到。如果沒(méi)有在那里找到它,JavaScript 將產(chǎn)生一個(gè)錯(cuò)誤,指出該屬性是 undefined。

由于所有原型屬性都是對(duì)象,因此鏈中的最終鏈接是 Object.prototype。沒(méi)有其他可以檢查的構(gòu)造函數(shù)原型屬性。

前面有一整章將原型鏈分解為更小的部分,所以如果你完全不明白這一點(diǎn),請(qǐng)閱讀該章,然后再回到這個(gè)解釋來(lái)鞏固你的理解。從這篇簡(jiǎn)短的文章中,我希望您明白,當(dāng)找不到屬性時(shí)(并被視為 undefined),JavaScript 將查看幾個(gè)原型對(duì)象來(lái)確定屬性是 undefined。查找總是會(huì)發(fā)生,這個(gè)查找過(guò)程就是 JavaScript 處理繼承以及簡(jiǎn)單屬性查找的方式。


使用 hasOwnProperty 驗(yàn)證對(duì)象屬性不是來(lái)自原型鏈

雖然 in 運(yùn)算符可以檢查對(duì)象的屬性,包括來(lái)自原型鏈的屬性,但 hasOwnProperty 方法可以檢查對(duì)象的屬性是否來(lái)自原型鏈。

在下面的示例中,我們想知道 myObject 是否包含屬性 foo,并且它沒(méi)有從原型鏈繼承該屬性。為此,我們?cè)儐?wèn) myObject 是否有自己的名為 foo 的屬性。

示例:sample41.html

 <script>  	var myObject = {foo: 'value'};  	console.log(myObject.hasOwnProperty('foo')) // Logs true.  	// Versus a property from the prototype chain. 	console.log(myObject.hasOwnProperty('toString'));  // Logs false.  </script>

當(dāng)您需要確定屬性是對(duì)象的本地屬性還是從原型鏈繼承時(shí),應(yīng)該利用 hasOwnProperty 方法。


使用 in 運(yùn)算符檢查對(duì)象是否包含給定屬性

in 運(yùn)算符用于驗(yàn)證(true 或 false)對(duì)象是否包含給定屬性。在此示例中,我們檢查 foo 是否是 myObject 中的屬性。

示例:sample42.html

 <script>  	var myObject = { foo: 'value' }; 	console.log('foo' in myObject); // Logs true.  </script>

您應(yīng)該知道 in 運(yùn)算符不僅檢查引用的對(duì)象中包含的屬性,還檢查對(duì)象通過(guò) prototype 鏈繼承的任何屬性。因此,應(yīng)用相同的屬性查找規(guī)則,如果當(dāng)前對(duì)象中沒(méi)有該屬性,則將在 prototype 鏈上搜索該屬性。

這意味著上一個(gè)示例中的 myObject 實(shí)際上通過(guò) prototype 鏈 (Object.prototype.toString) 包含一個(gè) toString 屬性方法,即使我們沒(méi)有指定一個(gè)(例如 myObject.toString) = ‘foo’)。

示例:sample43.html

 <script>  	var myObject = { foo: 'value' }; 	console.log('toString' in myObject); // Logs true.  </script>

在最后一個(gè)代碼示例中,toString 屬性實(shí)際上并不位于 myObject 對(duì)象內(nèi)部。但是,它是從 Object.prototype 繼承的,因此 in 運(yùn)算符得出的結(jié)論是 myObject 實(shí)際上具有繼承的 toString() 屬性方法。


使用 for in 循環(huán)枚舉(循環(huán))對(duì)象的屬性

通過(guò)使用 for in,我們可以循環(huán)訪問(wèn)對(duì)象中的每個(gè)屬性。在以下示例中,我們使用 for in 循環(huán)從 cody 對(duì)象中檢索屬性名稱(chēng)。

示例:sample44.html

 <script>  	var cody = { 		age: 23, 		gender: 'male' 	};  	for (var key in cody) { // key is a variable used to represent each property name.  		// Avoid properties inherited from the prototype chain. 		if (cody.hasOwnProperty(key)) { 			console.log(key); 		} 	}  </script>

for in 循環(huán)有一個(gè)缺點(diǎn)。它不僅會(huì)訪問(wèn)正在循環(huán)的特定對(duì)象的屬性。它還將在循環(huán)中包含對(duì)象繼承(通過(guò)原型鏈)的任何屬性。因此,如果這不是期望的結(jié)果,而且大多數(shù)情況下都不是,我們必須在循環(huán)內(nèi)使用簡(jiǎn)單的 if 語(yǔ)句來(lái)確保我們只訪問(wèn)我們正在循環(huán)的特定對(duì)象中包含的屬性。這可以通過(guò)使用所有對(duì)象繼承的 hasOwnProperty() 方法來(lái)完成。

在循環(huán)中訪問(wèn)屬性的順序并不總是在循環(huán)中定義它們的順序。此外,您定義屬性的順序不一定是訪問(wèn)它們的順序。

只有可枚舉的屬性(即在循環(huán)對(duì)象屬性時(shí)可用)才顯示在 for in 循環(huán)中。例如,構(gòu)造函數(shù)屬性將不會(huì)顯示。可以使用 propertyIsEnumerable() 方法檢查哪些屬性是可枚舉的。


主機(jī)對(duì)象和本機(jī)對(duì)象

您應(yīng)該知道,執(zhí)行 JavaScript 的環(huán)境(例如 Web 瀏覽器)通常包含所謂的主機(jī)對(duì)象。宿主對(duì)象不是 ecmascript 實(shí)現(xiàn)的一部分,但在執(zhí)行期間可作為對(duì)象使用。當(dāng)然,宿主對(duì)象的可用性和行為完全取決于宿主環(huán)境提供的內(nèi)容。

例如,在網(wǎng)絡(luò)瀏覽器環(huán)境中,window/head 對(duì)象及其所有包含對(duì)象(不包括 JavaScript 提供的對(duì)象)都被視為宿主對(duì)象。

在下面的示例中,我檢查 window 對(duì)象的屬性。

示例:sample45.html

 <script>  	for (x in window) { 		console.log(x); // Logs all of the properties of the window/head object. 	}  </script>

您可能已經(jīng)注意到,本機(jī) JavaScript 對(duì)象未在主機(jī)對(duì)象中列出。瀏覽器區(qū)分主機(jī)對(duì)象和本機(jī)對(duì)象是相當(dāng)常見(jiàn)的。

就 Web 瀏覽器而言,所有托管對(duì)象中最著名的是用于處理 HTML 文檔的界面,也稱(chēng)為 dom。以下示例是列出瀏覽器環(huán)境提供的 window.document 對(duì)象內(nèi)包含的所有對(duì)象的方法。

示例:sample46.html

 <script>  	for (x in window.document) { 		console.log(); 	}  </script>

我希望您在這里了解的是 JavaScript 規(guī)范本身并不關(guān)心宿主對(duì)象,反之亦然。 JavaScript 提供的內(nèi)容(例如,JavaScript 1.5、ECMA-262、第 3 版與 Mozilla 的 JavaScript 1.6、1.7、1.8、1.8.1、1.8.5)和主機(jī)環(huán)境提供的內(nèi)容之間存在一條分界線,并且這兩者不應(yīng)該存在感到困惑。

運(yùn)行 JavaScript 代碼的主機(jī)環(huán)境(例如 Web 瀏覽器)通常提供頭對(duì)象(例如 Web 瀏覽器中的 window 對(duì)象),其中語(yǔ)言的本機(jī)部分與主機(jī)對(duì)象(例如 一起存儲(chǔ)) window.location(Web 瀏覽器中的 window.location)和用戶定義的對(duì)象(例如,您編寫(xiě)的在 Web 瀏覽器中運(yùn)行的代碼)。

有時(shí),網(wǎng)絡(luò)瀏覽器制造商作為 JavaScript 解釋器的宿主,會(huì)在獲得批準(zhǔn)之前推出 JavaScript 版本或添加未來(lái)的 JavaScript 規(guī)范(例如,Mozilla 的 firefox JavaScript 1.6、1.7、1.8、1.8.1) ,1.8.5)。


使用 Underscore.js 增強(qiáng)和擴(kuò)展對(duì)象

當(dāng)需要認(rèn)真操作和管理對(duì)象時(shí),JavaScript 1.5 有所欠缺。如果您在 Web 瀏覽器中運(yùn)行 JavaScript,那么當(dāng)您需要比 JavaScript 1.5 提供的更多功能時(shí),我想在這里大膽建議使用 Underscore.js。 Underscore.js 在處理對(duì)象時(shí)提供以下功能。

這些函數(shù)適用于所有對(duì)象和數(shù)組:

  • each()
  • map()
  • reduce()
  • reduceRight()
  • 檢測(cè)()
  • 選擇()
  • reject()
  • all()
  • any()
  • include()
  • 調(diào)用()
  • pluck()
  • max()
  • min()
  • sortBy()
  • sortIndex()
  • toArray()
  • size()

這些函數(shù)適用于所有對(duì)象:

  • keys()
  • values()
  • 函數(shù)()
  • extend()
  • 克隆()
  • tap()
  • isEqual()
  • isEmpty()
  • isElement()
  • isArray()
  • isArguments
  • isFunction()
  • isString()
  • isNumber
  • isBoolean
  • isDate
  • isRegExp
  • isNaN
  • isNull
  • isUn??defined

結(jié)論

我喜歡這個(gè)庫(kù),因?yàn)樗昧藶g覽器支持的 JavaScript 的新本機(jī)添加功能,而且還為不支持的瀏覽器提供了相同的功能,所有這些都無(wú)需更改 JavaScript 的本機(jī)實(shí)現(xiàn),除非必須這樣做。 p>

開(kāi)始使用 Underscore.js 之前,請(qǐng)確保您的代碼中可能已使用的 JavaScript 庫(kù)或框架尚未提供您所需的功能。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊8 分享