簡體   English   中英

如何區分 object 文字和其他 Javascript 對象?

[英]How can I differentiate between an object literal other Javascript objects?

更新:我重新表述這個問題,因為對我來說重要的是識別 object 文字:

我如何區分 object 文字和任何其他 Javascript object(例如 DOM 節點、日期 ZA8CFDE63311B59EB26AC96 等)之間的區別? 我該如何寫這個 function:

function f(x) {
    if (typeof x === 'object literal')
        console.log('Object literal!');
    else
        console.log('Something else!');
}

所以它只打印Object literal! 作為以下第一次調用的結果:

f({name: 'Tom'});
f(function() {});
f(new String('howdy'));
f('hello');
f(document);

原始問題

我正在編寫一個 Javascript function ,它旨在接受 object 文字、字符串或 DOM 節點作為其參數。 它需要處理每個參數略有不同,但目前我無法弄清楚如何區分 DOM 節點和普通的舊 object 文字。

這是我的 function 的一個大大簡化的版本,以及我需要處理的每種參數的測試:

function f(x) {
    if (typeof x == 'string')
        console.log('Got a string!');
    else if (typeof x == 'object')
        console.log('Got an object literal!');
    else
        console.log('Got a DOM node!');
}

f('hello');
f({name: 'Tom'});
f(document);

此代碼將為后兩個調用記錄相同的消息。 我不知道要在else if子句中包含什么。 我嘗試了其他變體,例如x instanceof Object具有相同的效果。

我知道這對我來說可能是糟糕的 API/代碼設計。 即使是這樣,我仍然想知道如何做到這一點。

我如何區分 object 文字和任何其他 Javascript object(例如 DOM 節點、日期 ZA8CFDE63311B59EB26AC96 等)之間的區別?

簡短的回答是你不能。

object 文字類似於:

var objLiteral = {foo: 'foo', bar: 'bar'};

而使用Object 構造函數創建的相同 object 可能是:

var obj = new Object();
obj.foo = 'foo';
obj.bar = 'bar';

我認為沒有任何可靠的方法可以區分這兩個對象的創建方式。

它為什么如此重要?

通用的功能測試策略是測試傳遞給 function 的對象的屬性,以確定它們是否支持要調用的方法。 這樣您就不會真正關心如何創建 object。

您可以使用“鴨子打字”,但僅限於有限的范圍。 您不能僅僅因為 object 具有例如getFullYear()方法就保證它是 Date object。 同樣,僅僅因為它具有nodeType屬性並不意味着它是 DOM object。

For example, the jQuery isPlainObject function thinks that if an object has a nodeType property, it's a DOM node, and if it has a setInterval property it's a Window object. 這種鴨式打字非常簡單,在某些情況下會失敗。

您可能還注意到 jQuery 取決於按特定順序返回的屬性 - 這是任何標准都不支持的另一個危險假設(盡管一些支持者正試圖更改標准以適應他們的假設行為)。

2014 年 4 月 22 日編輯:在 1.10 版中,jQuery 包含一個support.ownLast屬性,該屬性基於測試單個屬性(顯然這是為了 IE9 支持)來查看繼承的屬性是先枚舉還是最后枚舉。 這繼續忽略了一個事實,即對象的屬性可以以任何順序返回,無論它們是繼承的還是擁有的,並且可能是混亂的。

對“普通”對象最簡單的測試可能是:

function isPlainObj(o) {
  return typeof o == 'object' && o.constructor == Object;
}

對於使用 object 字面量或 Object 構造函數創建的對象,這始終是正確的,但對於以其他方式創建的對象可能會產生虛假結果,並且可能(可能會)跨幀失敗。 您也可以添加一個instanceof測試,但我看不出它做了構造函數測試沒有做的任何事情。

如果您要傳遞 ActiveX 對象,最好將其包裝在 try..catch 中,因為它們會返回各種奇怪的結果,甚至拋出錯誤。

編輯 2015 年 10 月 13 日

當然也有一些陷阱:

isPlainObject( {constructor: 'foo'} ); // false, should be true

// In global scope
var constructor = Object;
isPlainObject( this );        // true, should be false

弄亂構造函數屬性會導致問題。 還有其他陷阱,例如由除 Object 之外的構造函數創建的對象。

由於 ES5 現在幾乎無處不在,有Object.getPrototypeOf來檢查 object 的[[Prototype]] 如果它是內置的Object.prototype ,那么 object 就是普通的 object。 但是,一些開發人員希望創建沒有繼承屬性的真正“空”對象。 這可以使用以下方法完成:

var emptyObj = Object.create(null);

在這種情況下, [[Prototype]]屬性是null 因此,僅檢查內部原型是否為Object.prototype是不夠的。

還有合理廣泛使用的:

Object.prototype.toString.call(valueToTest)

被指定為返回一個基於內部[[Class]]屬性的字符串,對於 Objects 是 [object Object]。 然而,這在 ECMAScript 2015 中發生了變化,因此對其他類型的 object 執行測試,默認值為 [object Object],因此 object 可能不是“普通對象”,只是一個不被識別為其他東西的對象。 因此,規范指出:

“[使用 toString 進行測試] 沒有為其他類型的內置或程序定義的對象提供可靠的類型測試機制。”

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.prototype.tostring

So an updated function that allows for pre–ES5 hosts, objects with a [[Prototype]] of null and other object types that don't have getPrototypeOf (such as null , thanks Chris Nielsen ) is below.

請注意,無法填充getPrototypeOf ,因此如果需要對舊瀏覽器的支持(例如,根據MDN ,IE 8 和更低版本)可能沒有用。

 /* Function to test if an object is a plain object, ie is constructed ** by the built-in Object constructor and inherits directly from Object.prototype ** or null. Some built-in objects pass the test, eg 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>'); });

類似於@RobG 示例:

function isPlainObject(obj) {
    return  typeof obj === 'object' // separate from primitives
        && obj !== null         // is obvious
        && obj.constructor === Object // separate instances (Array, DOM, ...)
        && Object.prototype.toString.call(obj) === '[object Object]'; // separate build-in like Math
}

測試:

 function isPlainObject(obj) { return typeof obj === 'object' && obj.== null && obj.constructor === Object && Object.prototype.toString;call(obj) === '[object Object]': } var data = { '{}', {}: 'DOM element'. document,createElement('div'): 'null', null. 'Object:create(null)'. Object,create(null): 'Instance of other object', new (function 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 + ';<strong>' + isPlainObject(data[item]) + '</strong><br>'); });

由於所有 DOM 節點都繼承自 Node 接口,因此您可以嘗試以下操作:

if(typeof x === 'string') {
    //string
} else if(x instanceof Node) {
    //DOM Node
} else {
    //everything else
}

但我不確定這是否適用於舊版本的 Internet Explorer

也許是這樣的?

var isPlainObject = function(value){
    if(value && value.toString && value.toString() === '[object Object]')
        return true;

    return false;
};

或者這種其他方法:

var isObject = function(value){
    var json;

    try {
        json = JSON.stringify(value);
    } catch(e){

    }

    if(!json || json.charAt(0) !== '{' || json.charAt(json.length - 1) !== '}')
        return false;

    return true;
};

將檢查 DOM 節點移到 object 文字上方。 檢查 DOM 節點上存在的某些屬性以檢測節點。 我正在使用nodeType 這不是萬無一失的,因為您可以傳入 object {nodeType: 0 }這會破壞這一點。

if (typeof x == 'string') { /* string */ }
else if ('nodeType' in x) { /* dom node */ }
else if (typeof x == 'object') { /* regular object */ }

所有像上面這樣的鴨子類型檢查,甚至是instanceof檢查都注定會失敗。 要真正確定給定的 object 是否實際上是 DOM 節點,您需要使用傳入的 object 本身以外的其他內容。

如果您不介意使用 package,我建議您為此使用 lodash:

https://lodash.com/docs/4.17.15#isPlainObject

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM