How should I detect if the argument is an array because typeof [] returns 'object' and I want to distinguish between arrays and objects.
It is possible that object will look like {"0":"string","1":"string","length":"2"} but I don't want it to come out as an array if it is in fact an object looking like an array.
JSON.parse and JSON.stringify are able to make this distinction. How can I do it?
I am using Node.JS which is based on V8 the same as Chrome.
Array.isArray
native V8 function. It's fast, it's always correct. This is part of ES5.
arr instanceof Array
Checks whether the object was made with the array constructor.
_.isArray // underscore method.
A method from underscore. Here is a snippet taken from the their source
var toString = Object.prototype.toString,
nativeIsArray = Array.isArray;
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
This method takes an object and calls the Object.prototype.toString method on it. This will always return [object Array] for arrays.
In my personal experience I find asking the toString method is the most effective but it's not as short or readable as instanceof Array nor is it as fast as Array.isArray but that's ES5 code and I tend to avoid using it for portability.
I would personally recommend you try using underscore, which is a library with common utility methods in it. It has a lot of useful functions that DRY up your code.
Try this code:
Array.isArray(argument)
How about:
your_object instanceof Array
In V8 in Chrome I get
[] instanceof Array
> true
({}) instanceof Array
> false
({"0":"string","1":"string","length":"2"}) instanceof Array
> false
I looks like this question has several good answers, but for completeness I would add another option, which have not been suggested earlier.
In order to check if something is an array, you can use Node.js util native module and its isArray() function.
Example:
var util = require('util');
util.isArray([]); // true
util.isArray(new Array); // true
util.isArray({"0":"string","1":"string","length":"2"}); // false
With that method you do not have to worry about JS standards implemented by V8 as it will always show the right answer.
Try this way:
console.log(Object.prototype.toString.call(arg).replace(/^[object (.+)]$/, '$1').toLowerCase())
Related
CLARIFICATION to moderator
As some moderators are a whee bit quick when scanning questions, I have to underline that I am not asking why to use Object.prototype.hasOwnProperty.call instead of myObject.hasOwnProperty. The devil is in the details.
The ESLint rule no-prototype-builtins forbids the use of built-ins coming from the Object prototype. It's there to provide you against code such as this:
function serverMethodToParseClientInput(json){
const o = JSON.parse(json);
if (o.hasOwnProperty("foo")) {
doSomethingMeaningful();
}
}
const stringFromClient = "{\"hasOwnProperty\":1, \"reason\": \"Crash your system\"}";
serverMethodToParseClientInput(stringFromClient);
Trying to call the method on objects created with null as their prototype will also fail, which I think is an even more reasonable thing to guard against. Example: const o = Object.create(null);.
Instead of obj.hasOwnProperty(field), you are supposed to use Object.prototype.hasOwnProperty.call(obj, field). I don't really see the difference between this and Object.hasOwnProperty.call(obj, field). Granted, Object is not its own prototype, so there is a difference of sorts, but since you can overwrite the props on either object, there isn't really much of a safeguard here, IMHO.
So I'm wondering if there is any point in reaching for the prototype of Object when Object will do?
Using Object.hasOwnProperty is weird because it makes it seem like you are using a static method like Object.assign().
It still works because if a property is not found on Object, it falls back to Function.prototype object. And, this object inherits from Object.prototype. So, if you have a custom implementation for Function.prototype.hasOwnProperty, then Object.hasOwnProperty will use that implementation instead of Object.prototype.hasOwnProperty
console.log( Object.hasOwnProperty === Object.prototype.hasOwnProperty ) // true
console.log( Object.getPrototypeOf(Object) === Function.prototype ) // true
console.log( Object.getPrototypeOf(Function.prototype) === Object.prototype ) // true
Function.prototype.hasOwnProperty = _ => 'custom implementaion in Function.prototype'
const obj = { prop: 10 }
console.log(Object.hasOwnProperty.call(obj, 'prop')) // custom implementaion
console.log(Object.prototype.hasOwnProperty.call(obj, 'prop')) // true
Note: The difference between Object.prototype.hasOwnProperty.call(myObj, prop) and myObj.hasOwnProperty(prop) is explained here
After testing out instasnceof I found that it will return true if the argument is an array or an object literal.
function test(options){
if(options instanceof Object){alert('yes')}//this will alert for both arrays and object literals
}
test({x:11})// alerts
test([11])// alerts as well but I do not want it to
Is there a way to test if the argument "options" is an object literal?
P.S. I am creating a module that will allow the user to access its configuration options, and I want to test if the argument is only an object literal or not?
is there a way to test if the argument "options" is an object literal?
No, because it makes no sense. You can test whether it's an object, but how it was created (via a literal in the call to your function, via a literal elsewhere, through new Object, by deserializing a JSON string, ...) is not information that's maintained.
after testing out instasnceof i found that it will return true if the argument is an array or an object literal
Correct. Arrays in JavaScript are objects (and not really arrays).
If you want to test that an object is a plain old object, you can do this:
if (Object.prototype.toString.call(options) === "[object Object]") {
// It's a plain object
}
But there's really no reason to do that. It's not your problem. As long as what they pass you has the properties you expect, don't try to limit the object further.
p.s. i'm making a module that will allow the user to pass it configuration options and i want to test to make sure that the argument is only an object literal.
Why? If the user wants to use an object that hasn't been declared as a literal right there and then, why would you care? If they want to use an object that they've created via a different constructor function (e.g., rather than just a plain object), again, why would you care?
function isPlainObject(o) {
return Object(o) === o && Object.getPrototypeOf(o) === Object.prototype;
}
However, you can't test wether o was declared as a literal or instantiated somehow else - you can just test whether it's a plain object without any constructor than Object.
If you're trying to forbid arrays, you can just do this:
var isObject = options instanceof Object;
var isArray = options instanceof Array;
if(isObject && !isArray)
{
alert('yes');
}
An alternative solution would be to use Lodash:
_.isPlainObject(value)
Here is the documentation:
https://lodash.com/docs/4.17.15#isPlainObject
Given the developments of JavaScript since the languages' inception, why is there not a built in method that checks if an object is a plain object?
Or does the method in fact exist?
You can check the type and the instance of an object this way:
var a = new Date();
console.log(typeof a);
console.log(a instanceof Date);
var b = "Hello";
console.log(typeof b);
console.log(b instanceof Date);
Updated according to the comments from the OP:
let arr = [1, 2, true, 4, {
"abc": 123
},
6, 7, {
"def": 456
},
9, [10], {}, "[object Object]"
];
arr.forEach(function(v) {
if (typeof v == "object" && !(v instanceof Array) && v != null)
console.log("Object Found");
else
; // console.log("Na");
});
The above code snippets outputs thrice Object Found.
There doesn't exist any explicit direct way to check if a value is an object, i.e. belongs to Object type, but there are some foolproof ways to do it. I wrote a list in another answer, the most succinct seems
function isObject(value) {
return Object(value) === value;
}
A feature like this has been requested multiple times on esdiscuss. For example,
What is an Object Type(O)?
Juriy Zaytsev "kangax" wonders about a proper way to check if a value is an object.
typeof null
Brendan Eich: "I think we should consider Object.isObject"
Jorge: "Why not .isPrimitive()?"
ES6 doesn't need opt-in
Brendan Eich: "We want sane isObject and isNull predicates"
Axel Rauschmayer: "predicates such as isObject() and isPrimitive()"
In fact, Object.isObject was proposed as strawman, and it appeared in an ES6 early draft.
TC39 bashing: Discussion about Object.isObject in the ES6 draft.
How primitive are Symbols? Bignums? etc: discusses x === Object(x)
Object.isObject strawman was eventually rejected and removed from ES6 draft.
More recently,
ES8 Proposal: Optional Static Typing (Brandon Andrews): Includes Object.isObject
Now there is the is{Type} Methods stage 0 proposal which includes Object.isObject among lots of various other checks.
So there is still hope and eventually we may have something like this.
The above is for testing objects in general. If you don't want that you should define what "plain object" means for you.
For example, you can test the constructor property. But any object can customize it.
You can use Object.prototype.toString to get the legacy ES5 [[Class]]. But any object can customize that via Symbol.toStringTag.
You can check the value returned by [[GetPrototypeOf]]. But even exotic objects might allow their prototype to be changed to whatever arbitrary object or null. And Proxy objects even have full control over that internal method.
So most probably you won't be able to rely on these tests. And adding something to the standard may be hard because different people may want different things.
What I would like is some way to check if an object is an ordinary one. That is, it has the default behaviour for the essential internal methods that must be supported by all objects.
Once you know that an object is ordinary, you can rely on things like [[GetPrototypeOf]] to customize the test to your tastes.
Relying on [object Object] string representation is inaccurate. This behaviour may be changed for any objects with:
let o = { toString: () => '...' };
('' + o) !== '[object Object]'
var a = [];
a.toString = () => '[object Object]';
('' + a) === '[object Object]';
The most solid way to check if a value is a plain object is
let o = {}
Object.getPrototypeOf(o) === Object.prototype
And considering that constructor property wasn't tampered, the most straightforward way to check if a value is a plain object is
let o = {}
o.constructor === Object
This covers all POJOs constructed from Object and doesn't cover Object.create(null, { ... }) or any child classes (including built-ins like RegExp or Array):
Object.create(null).constructor !== Object
[].constructor !== Object
(new class {}).constructor !== Object
One of the possible reasons why there is no dedicated method to check for object plainness is because a restriction to use only {} objects is not practical. This makes very little sense in the context of JS. This prevents the use of any class instances or relatively 'plain' objects (Object.create({}, ...)).
This would require the hack in order for desired non-plain objects to pass the check:
Object.assign({}, (new class {})).constructor === Object
In most cases of object checking 'everything which is not forbidden is allowed' principle pays off (with extra caution regarding infamous null inconsistency).
Applying the above to this case, a safe and concise condition to filter non-array objects is
o && typeof o === 'object' && !Array.isArray(o)
And a condition to filter objects that are not built-ins (functions, Array, RegExp, etc) is
o && (o.constructor === Object || !/\[native code\]/.test(o.constructor))
Just for the sake of further documenting different ways:
One way I can think of:
JSON.stringify(testValue)[0] === '{';
Keep in mind that objects with circular references cannot be stringified. However, if you are certain that all testValues cannot have circular references, you have yourself a way to check against Null, Arrays, and any primitive value to ensure that you have an Object.
I suggest that if you plan on using this throughout your code though, that you define a helper function that actually implements it, in case you find that this does not work as you expect and end up having to change the way you check it.
Every thing JavaScript is an Object , so there is no need to have an isObject api
In order to make the syntax for one of my functions nicer, I need to be able to tell whether a specific parameter is an array or "hash" (which I know are just objects).
Typeof doesn't work, because they both return the same thing
typeof {foo:"bar"} // Object
typeof ["foo","bar"] // Object
So how would I differentiate between the two?
I know this works, but I'm hoping there's a nicer way
({foo:"bar"}).constructor // Object()
(["foo","bar"]).constructor // [ undefined ]
EDIT
Ah, it seems [ undefined ] in firebug is the same thing as Array. Kind of weird.
You could check the length property as SLaks suggested, but as soon as you pass it a function object you'll be surprised, because it in fact has a length property. Also if the object has a length property defined, you'll get wrong result again.
Your best bet is probably:
function isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
}
jQuery uses it, and a "couple of" other people... :)
It is more fail proof than the instanceof way. The method is also suggested by the following article:
'instanceof' considered harmful (or how to write a robust 'isArray') (#kagax)
Another thing to add that this function is almost identical to the Array.isArray function in ES 5 spec:
15.4.3.2 Array.isArray ( arg )
If Type(arg) is not Object, return
false.
If the value of the [[Class]]
internal property of arg is "Array",
then return true.
Return false.
something instanceof Array works fine within a single document, but will fail if you start passing arrays between different windows, because the Array from one window is a different object from Array on another. If you have no intention of doing cross-window-scripting (and in general, it's worth avoiding) I would recommend sticking with this.
If you need cross-window support, things are a bit more complex. In the future the story is simple, as ECMAScript Fifth Edition defines a function to do exactly this:
Array.isArray([1]); // -> true
You should use this functionality where available as it's the only reliable and standards-endorsed way. However, many of today's browsers don't support it yet.
Where it isn't available you have to rely on Object#toString serialisation, which is ugly and slightly dodgy. Although it will in general work reliably with today's browsers, there are imaginable cases where it might not (primarily to do with host objects).
You can hack this fallback method into Array on browsers that don't support it, and then use Array.isArray at all times:
if (!('isArray' in Array)) {
Array.isArray= function(o) {
return Object.prototype.toString.call(o)==='[object Array]';
};
}
As for constructor, never use it. It's not available everywhere and it doesn't do what you think. Using it is almost always a mistake.
I think the most elegant way is to simply use the instanceof operator:
if (myVar instanceof Array)
doSomething();
examples:
[] instanceof Array // true
{} instanceof Array // false
{length:100} instanceof Array // false
null instanceof Array // false
Edit:
Be aware that it will fail when testing an object from another iFrame (see answers by #galambalazs and #bobince)
Check for the length property:
"length" in {foo:"bar"} //false
"length" in ["foo","bar"] //true
This typeof function is used to correct the array/object clash for the typeof operator, and the null/object clash. It does not however work across frames or across windows. See the source for a more advanced version that works across these.
function typeOf(value) {
var s = typeof value;
if (s === 'object') {
if (value) {
if (value instanceof Array) {
s = 'array';
}
} else {
s = 'null';
}
}
return s;
}
Source: Douglas Crockford's Javascript site
I'm trying to figure out what's gone wrong with my json serializing, have the current version of my app with and old one and am finding some surprising differences in the way JSON.stringify() works (Using the JSON library from json.org).
In the old version of my app:
JSON.stringify({"a":[1,2]})
gives me this;
"{\"a\":[1,2]}"
in the new version,
JSON.stringify({"a":[1,2]})
gives me this;
"{\"a\":\"[1, 2]\"}"
any idea what could have changed to make the same library put quotes around the array brackets in the new version?
Since JSON.stringify has been shipping with some browsers lately, I would suggest using it instead of Prototype’s toJSON. You would then check for window.JSON && window.JSON.stringify and only include the json.org library otherwise (via document.createElement('script')…). To resolve the incompatibilities, use:
if(window.Prototype) {
delete Object.prototype.toJSON;
delete Array.prototype.toJSON;
delete Hash.prototype.toJSON;
delete String.prototype.toJSON;
}
The function JSON.stringify() defined in ECMAScript 5 and above (Page 201 - the JSON Object, pseudo-code Page 205), uses the function toJSON() when available on objects.
Because Prototype.js (or another library that you are using) defines an Array.prototype.toJSON() function, arrays are first converted to strings using Array.prototype.toJSON() then string quoted by JSON.stringify(), hence the incorrect extra quotes around the arrays.
The solution is therefore straight-forward and trivial (this is a simplified version of Raphael Schweikert's answer):
delete Array.prototype.toJSON
This produces of course side effects on libraries that rely on a toJSON() function property for arrays. But I find this a minor inconvenience considering the incompatibility with ECMAScript 5.
It must be noted that the JSON Object defined in ECMAScript 5 is efficiently implemented in modern browsers and therefore the best solution is to conform to the standard and modify existing libraries.
A possible solution which will not affect other Prototype dependencies would be:
var _json_stringify = JSON.stringify;
JSON.stringify = function(value) {
var _array_tojson = Array.prototype.toJSON;
delete Array.prototype.toJSON;
var r=_json_stringify(value);
Array.prototype.toJSON = _array_tojson;
return r;
};
This takes care of the Array toJSON incompatibility with JSON.stringify and also retains toJSON functionality as other Prototype libraries may depend on it.
Edit to make a bit more accurate:
The problem key bit of code is in the JSON library from JSON.org (and other implementations of ECMAScript 5's JSON object):
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
The problem is that the Prototype library extends Array to include a toJSON method, which the JSON object will call in the code above. When the JSON object hits the array value it calls toJSON on the array which is defined in Prototype, and that method returns a string version of the array. Hence, the quotes around the array brackets.
If you delete toJSON from the Array object the JSON library should work properly. Or, just use the JSON library.
I think a better solution would be to include this just after prototype has been loaded
JSON = JSON || {};
JSON.stringify = function(value) { return value.toJSON(); };
JSON.parse = JSON.parse || function(jsonsring) { return jsonsring.evalJSON(true); };
This makes the prototype function available as the standard JSON.stringify() and JSON.parse(), but keeps the native JSON.parse() if it is available, so this makes things more compatible with older browsers.
I'm not that fluent with Prototype, but I saw this in its docs:
Object.toJSON({"a":[1,2]})
I'm not sure if this would have the same problem the current encoding has, though.
There's also a longer tutorial about using JSON with Prototype.
This is the code I used for the same issue:
function stringify(object){
var Prototype = window.Prototype
if (Prototype && Prototype.Version < '1.7' &&
Array.prototype.toJSON && Object.toJSON){
return Object.toJSON(object)
}
return JSON.stringify(object)
}
You check if Prototype exists, then you check the version. If old version use Object.toJSON (if is defined) in all other cases fallback to JSON.stringify()
Here's how I'm dealing with it.
var methodCallString = Object.toJSON? Object.toJSON(options.jsonMethodCall) : JSON.stringify(options.jsonMethodCall);
My tolerant solution checks whether Array.prototype.toJSON is harmful for JSON stringify and keeps it when possible to let the surrounding code work as expected:
var dummy = { data: [{hello: 'world'}] }, test = {};
if(Array.prototype.toJSON) {
try {
test = JSON.parse(JSON.stringify(dummy));
if(!test || dummy.data !== test.data) {
delete Array.prototype.toJSON;
}
} catch(e) {
// there only hope
}
}
As people have pointed out, this is due to Prototype.js - specifically versions prior to 1.7. I had a similar situation but had to have code that operated whether Prototype.js was there or not; this means I can't just delete the Array.prototype.toJSON as I'm not sure what relies on it. For that situation this is the best solution I came up with:
function safeToJSON(item){
if ([1,2,3] === JSON.parse(JSON.stringify([1,2,3]))){
return JSON.stringify(item); //sane behavior
} else {
return item.toJSON(); // Prototype.js nonsense
}
}
Hopefully it will help someone.
If you don't want to kill everything, and have a code that would be okay on most browsers, you could do it this way :
(function (undefined) { // This is just to limit _json_stringify to this scope and to redefine undefined in case it was
if (true ||typeof (Prototype) !== 'undefined') {
// First, ensure we can access the prototype of an object.
// See http://stackoverflow.com/questions/7662147/how-to-access-object-prototype-in-javascript
if(typeof (Object.getPrototypeOf) === 'undefined') {
if(({}).__proto__ === Object.prototype && ([]).__proto__ === Array.prototype) {
Object.getPrototypeOf = function getPrototypeOf (object) {
return object.__proto__;
};
} else {
Object.getPrototypeOf = function getPrototypeOf (object) {
// May break if the constructor has been changed or removed
return object.constructor ? object.constructor.prototype : undefined;
}
}
}
var _json_stringify = JSON.stringify; // We save the actual JSON.stringify
JSON.stringify = function stringify (obj) {
var obj_prototype = Object.getPrototypeOf(obj),
old_json = obj_prototype.toJSON, // We save the toJSON of the object
res = null;
if (old_json) { // If toJSON exists on the object
obj_prototype.toJSON = undefined;
}
res = _json_stringify.apply(this, arguments);
if (old_json)
obj_prototype.toJSON = old_json;
return res;
};
}
}.call(this));
This seems complex, but this is complex only to handle most use cases.
The main idea is overriding JSON.stringify to remove toJSON from the object passed as an argument, then call the old JSON.stringify, and finally restore it.