我怎樣才能告訴對象字面量和任何其他Javascript對象(例如,DOM節點,Date對象等)之間的區別?
簡短的回答是你不能。
的對象字面是一樣的東西:
var objLiteral = {foo: 'foo', bar: 'bar'};
而同一對象使用對象構造函數創建可能是:
var obj = new Object();
obj.foo = 'foo';
obj.bar = 'bar';
我不認爲有任何可靠如何區分這兩個對象是如何創建的。
爲什麼它很重要?
一般功能測試策略是測試傳遞給函數的對象的屬性,以確定它們是否支持要調用的方法。這樣你就不會在乎如何創建一個對象。
你可以使用「鴨子打字」,但只能在有限的範圍內。你不能保證僅僅因爲一個對象有一個getFullYear()
方法,它是一個Date對象。同樣,僅僅因爲它具有nodeType屬性並不意味着它是一個DOM對象。
例如,jQuery的isPlainObject
函數認爲,如果一個對象都有一個nodeType屬性,它是一個DOM節點,如果它有一個setInterval
財產這是一個Window對象。這種鴨子打字非常簡單,在某些情況下會失敗。
您可能還會注意到,jQuery依賴於以特定順序返回的屬性 - 另一個不被任何標準支持的危險假設(儘管一些支持者試圖改變標準以適應他們的假定行爲)。
編輯22-APR-2014:在版本1.10的jQuery包括基於測試單個屬性(顯然這是IE9支持),看看是否繼承屬性列舉第一個或最後一個support.ownLast屬性。這繼續忽略這樣一個事實,即一個對象的屬性可以返回任何的順序,無論它們是繼承的還是自己的,並且可能是混亂的。
可能是「普通」對象最簡單的測試:
function isPlainObj(o) {
return typeof o == 'object' && o.constructor == Object;
}
這將永遠成爲使用對象文本或Object構造函數創建的對象真實的,但很可能給出虛假的結果爲對象創建其他方式和可能(可能會)跨幀失敗。您也可以添加instanceof
測試,但是我看不到它執行的任何構造函數測試都沒有。
如果你傳遞的是ActiveX對象,最好把它包裝在try..catch中,因爲它們可以返回各種奇怪的結果,甚至會拋出錯誤。
編輯13 - 10月 - 2015年
當然也有一些陷阱:
isPlainObject({constructor: 'foo'}); // false, should be true
// In global scope
var constructor = Object;
isPlainObject(this); // true, should be false
與constructor屬性搞亂會導致一些問題。還有其他陷阱,例如由Object以外的構造函數創建的對象。
由於ES5現在幾乎無處不在,因此有Object.getPrototypeOf來檢查對象的[[Prototype]]
。如果它是插入Object.prototype,那麼該對象是一個普通的對象。但是,一些開發人員希望創建沒有繼承屬性的真正「空」對象。這是可以做到用:
var emptyObj = Object.create(null);
在這種情況下,[[Prototype]]
屬性爲空。因此只需檢查內部原型是否爲Object.prototype是不夠的。
也有相當廣泛用於:被指定爲返回基於內部[[Class]]
屬性,這對於對象是[對象的對象]將字符串
Object.prototype.toString.call(valueToTest)
。但是,在ECMAScript 2015中,這已經發生了變化,因此測試是針對其他類型的對象執行的,默認值是[object Object],因此該對象可能不是「普通對象」,而只是一個不被識別爲其他類型的對象。因此,該說明書中指出:
「不提供測試 可靠類型的機構對於其他種內置或程序定義的對象的[使用的toString測試]」
http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.prototype.tostring
所以更新的功能,允許進行預ES5主機,與空和其他對象類型的[[Prototype]]
沒有getPrototypeOf(如空對象,感謝Chris Nielsen ) 在下面。
注意,也沒有辦法來填充工具getPrototypeOf,因此,如果需要舊的瀏覽器支持可能沒有用(例如IE 8和下部,根據MDN)。
/* Function to test if an object is a plain object, i.e. is constructed
** by the built-in Object constructor and inherits directly from Object.prototype
** or null. Some built-in objects pass the test, e.g. Math which is a plain object
** and some host or exotic objects may pass also.
**
** @param {} obj - value to test
** @returns {Boolean} true if passes tests, false otherwise
*/
function isPlainObject(obj) {
// Basic check for Type object that's not null
if (typeof obj == 'object' && obj !== null) {
// If Object.getPrototypeOf supported, use it
if (typeof Object.getPrototypeOf == 'function') {
var proto = Object.getPrototypeOf(obj);
return proto === Object.prototype || proto === null;
}
// Otherwise, use internal class
// This should be reliable as if getPrototypeOf not supported, is pre-ES5
return Object.prototype.toString.call(obj) == '[object Object]';
}
// Not an object
return false;
}
// Tests
var data = {
'Host object': document.createElement('div'),
'null' : null,
'new Object' : {},
'Object.create(null)' : Object.create(null),
'Instance of other object' : (function() {function Foo(){};return new Foo()}()),
'Number primitive ' : 5,
'String primitive ' : 'P',
'Number Object' : new Number(6),
'Built-in Math' : Math
};
Object.keys(data).forEach(function(item) {
document.write(item + ': ' + isPlainObject(data[item]) + '<br>');
});
http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object – Ben 2011-05-03 22:19:49
@Ben,現在我已經更新了這個問題,我認爲這不是那個問題的重複。但如果它是另一個問題的重複,我會很樂意關閉它! – 2011-05-03 23:00:40
這幾乎是重複的,但有沒有解決方案在給定用戶定義的對象時不會導致誤報? http://stackoverflow.com/questions/4320767/check-that-value-is-object-literal – 2011-05-03 23:07:11