與對象和屬性一起工作

與對象和屬性一起工作

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


復雜對象

示例: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>

這里要學習的簡單概念是,復雜對象可以包含任何可以在 JavaScript 中名義上表達的內容。當您看到此操作完成時,您不應該感到驚訝,因為所有本機對象都可以發生變化。這甚至適用于對象形式的 String()、Number() 和 Boolean() 值,即使用 new 運算符創建它們時。


以編程方式有益的方式封裝復雜對象

Object()、Array() 和 Function() 對象可以包含其他復雜對象。在下面的示例中,我通過使用 Object() 對象設置對象樹來演示這一點。

示例: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() 對象(又名多維數組)或 Function() 對象完成同樣的操作。

示例: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>

這里要掌握的主要概念是,一些復雜對象被設計為以編程上有益的方式封裝其他對象。


使用點表示法或括號表示法獲取、設置和更新對象的屬性

我們可以使用點符號或方括號符號來獲取、設置或更新對象的屬性。

在下面的示例中,我演示了點表示法,這是通過使用對象名稱后跟句點,然后后跟要獲取、設置或更新的屬性來完成的(例如,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>

點表示法是獲取、設置或更新對象屬性的最常見表示法。

除非需要,否則括號表示法并不常用。在下面的示例中,我將上一個示例中使用的點符號替換為括號符號。對象名稱后跟一個左括號、屬性名稱(用引號引起來),然后是一個右括號:

示例: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>

當您需要訪問屬性鍵并且您必須使用包含表示屬性名稱的字符串值的變量時,括號表示法非常有用。在下一個示例中,我通過使用方括號表示法訪問屬性 foobar 來演示括號表示法相對于點表示法的優勢。我使用兩個變量來執行此操作,這兩個變量在連接時會生成 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>

此外,括號表示法可以方便地獲取無效 JavaScript 標識符的屬性名稱。在下面的代碼中,我使用一個數字和一個保留關鍵字作為屬性名稱(作為字符串有效),只有括號表示法才能訪問該屬性名稱。

示例: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>

因為對象可以包含其他對象,所以 cody.object.object.object.object 或 cody[‘object’][‘object’][‘object’][‘object’] 可以在以下位置查看次。這稱為對象鏈。對象的封裝可以無限期地進行下去。

對象在 JavaScript 中是可變的,這意味著可以隨時對大多數對象執行獲取、設置或更新它們。通過使用括號表示法(例如,cody[‘age’]),您可以模仿其他語言中的關聯數組

如果對象內的屬性是方法,您所要做的就是使用 () 運算符(例如 cody.getGender())來調用屬性方法。


刪除對象屬性

delete 運算符可用于完全刪除對象的屬性。在下面的代碼片段中,我們從 foo 對象中刪除 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 不會刪除在原型鏈上找到的屬性。

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


如何解析對對象屬性的引用

如果您嘗試訪問對象中未包含的屬性,JavaScript 將嘗試使用原型鏈查找該屬性或方法。在下面的示例中,我創建一個數組并嘗試訪問尚未定義的名為 foo 的屬性。您可能會認為,由于 myArray.foo 不是 myArray 對象的屬性,JavaScript 將立即返回 undefined。但是 JavaScript 會在另外兩個地方(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>

財產。如果它有該屬性,它將返回該屬性的值,并且不會發生繼承,因為原型鏈沒有被杠桿化。如果實例沒有該屬性,JavaScript 將在對象的構造函數 prototype 對象中查找它。

所有對象實例都有一個屬性,該屬性是創建該實例的構造函數的秘密鏈接(又名 __proto__)。可以利用這個秘密鏈接來獲取構造函數,特別是實例構造函數的原型屬性。

這是 JavaScript 中對象最令人困惑的方面之一。但讓我們來推理一下。請記住,函數也是具有屬性的對象。允許對象從其他對象繼承屬性是有意義的。就像說:“嘿,對象 B,我希望你分享對象 A 擁有的所有屬性。”默認情況下,JavaScript 通過 prototype 對象將這一切連接到本機對象。當您創建自己的構造函數時,您也可以利用原型鏈。

JavaScript 到底是如何實現這一點的?在您了解它的本質之前,您會感到困惑:只是一組規則。讓我們創建一個數組來更仔細地檢查 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() 實例是一個具有屬性和方法的對象。當我們訪問其中一種數組方法時,例如 join(),我們問自己:從 Array() 構造函數創建的 myArray 實例是否有自己的 join() 方法?我們來檢查一下。

示例:sample39.html

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

不,沒有。然而 myArray 可以訪問 join() 方法,就好像它是它自己的屬性一樣。這里發生了什么?好吧,您剛剛觀察了原型鏈的運行情況。我們訪問了一個屬性,盡管該屬性不包含在 myArray 對象中,但 JavaScript 可以在其他地方找到該屬性。其他地方是非常具體的。當 Array() 構造函數由 JavaScript 創建時,join() 方法被添加(除其他外)作為 Array() 的 prototype 屬性的屬性。

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

鏈條的終點在哪里?讓我們再次檢查該示例,在 myArray 上調用 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 對象中定義。因此,原型鏈接規則被調用,JavaScript 在 Array 構造函數原型屬性中查找屬性(例如,Array.prototype)。它也不存在,因此再次調用鏈式規則,我們在 Object() 原型屬性 (Object.prototype) 中查找該屬性。是的,它在那里找到。如果沒有在那里找到它,JavaScript 將產生一個錯誤,指出該屬性是 undefined。

由于所有原型屬性都是對象,因此鏈中的最終鏈接是 Object.prototype。沒有其他可以檢查的構造函數原型屬性。

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


使用 hasOwnProperty 驗證對象屬性不是來自原型鏈

雖然 in 運算符可以檢查對象的屬性,包括來自原型鏈的屬性,但 hasOwnProperty 方法可以檢查對象的屬性是否來自原型鏈。

在下面的示例中,我們想知道 myObject 是否包含屬性 foo,并且它沒有從原型鏈繼承該屬性。為此,我們詢問 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>

當您需要確定屬性是對象的本地屬性還是從原型鏈繼承時,應該利用 hasOwnProperty 方法。


使用 in 運算符檢查對象是否包含給定屬性

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

示例:sample42.html

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

您應該知道 in 運算符不僅檢查引用的對象中包含的屬性,還檢查對象通過 prototype 鏈繼承的任何屬性。因此,應用相同的屬性查找規則,如果當前對象中沒有該屬性,則將在 prototype 鏈上搜索該屬性。

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

示例:sample43.html

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

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


使用 for in 循環枚舉(循環)對象的屬性

通過使用 for in,我們可以循環訪問對象中的每個屬性。在以下示例中,我們使用 for in 循環從 cody 對象中檢索屬性名稱。

示例: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 循環有一個缺點。它不僅會訪問正在循環的特定對象的屬性。它還將在循環中包含對象繼承(通過原型鏈)的任何屬性。因此,如果這不是期望的結果,而且大多數情況下都不是,我們必須在循環內使用簡單的 if 語句來確保我們只訪問我們正在循環的特定對象中包含的屬性。這可以通過使用所有對象繼承的 hasOwnProperty() 方法來完成。

在循環中訪問屬性的順序并不總是在循環中定義它們的順序。此外,您定義屬性的順序不一定是訪問它們的順序。

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


主機對象和本機對象

您應該知道,執行 JavaScript 的環境(例如 Web 瀏覽器)通常包含所謂的主機對象。宿主對象不是 ecmascript 實現的一部分,但在執行期間可作為對象使用。當然,宿主對象的可用性和行為完全取決于宿主環境提供的內容。

例如,在網絡瀏覽器環境中,window/head 對象及其所有包含對象(不包括 JavaScript 提供的對象)都被視為宿主對象。

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

示例:sample45.html

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

您可能已經注意到,本機 JavaScript 對象未在主機對象中列出。瀏覽器區分主機對象和本機對象是相當常見的。

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

示例:sample46.html

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

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

運行 JavaScript 代碼的主機環境(例如 Web 瀏覽器)通常提供頭對象(例如 Web 瀏覽器中的 window 對象),其中語言的本機部分與主機對象(例如 一起存儲) window.location(Web 瀏覽器中的 window.location)和用戶定義的對象(例如,您編寫的在 Web 瀏覽器中運行的代碼)。

有時,網絡瀏覽器制造商作為 JavaScript 解釋器的宿主,會在獲得批準之前推出 JavaScript 版本或添加未來的 JavaScript 規范(例如,Mozilla 的 firefox JavaScript 1.6、1.7、1.8、1.8.1) ,1.8.5)。


使用 Underscore.js 增強和擴展對象

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

這些函數適用于所有對象和數組:

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

這些函數適用于所有對象:

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

結論

我喜歡這個庫,因為它利用了瀏覽器支持的 JavaScript 的新本機添加功能,而且還為不支持的瀏覽器提供了相同的功能,所有這些都無需更改 JavaScript 的本機實現,除非必須這樣做。 p>

開始使用 Underscore.js 之前,請確保您的代碼中可能已使用的 JavaScript 庫或框架尚未提供您所需的功能。

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享