How can I determine that an object is "custom" in Javascript? - javascript

To clarify my title, I need a way to determine that an object is not a String, Number, Boolean, or any other predefined JavaScript object. One method that comes to mind is this:
if(!typeof myCustomObj == "string" && !typeof myCustomObj == "number" && !typeof myCustomObj == "boolean") {
I could check to see if myCustomObj is an object, like this:
if(typeof myCustomObj == "object") {
This only works for primitive values, though, as this typeof new String("hello world") == "object") is true.
What is a reliable way to determine whether or not an object is not a predefined JavaScript object?

Here's is how jQuery does it in jQuery.isPlainObject()
function (obj) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if (!obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow(obj)) {
return false;
}
try {
// Not own constructor property must be Object
if (obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
} catch(e) {
// IE8,9 Will throw exceptions on certain host objects #9897
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for (key in obj) {}
return key === undefined || hasOwn.call(obj, key);
}

You can use the "toString" functon on the Object prototype:
var typ = Object.prototype.toString.call( someTestObject );
That gives answers like "[object String]" or "[object Date]" for the built-in types. Unfortunately you can't distinguish that way between things created as plain Object instances and things made with a constructor, but in a sense those things aren't really that much different anyway.

Related

Is there a way to check for just a plain vanilla javascript object as opposed to any other special kind of object (i.e. Date)?

I tried using instanceof but it can't differentiate between {}, [], and Date.
For example, If I want to make an if tree that goes like this:
function foo(someVar){
if (/*someVar is an {}*/) {
} else if (/*someVar is an []*/) {
} else { //someVar could be a String or a Date or anything
}
}
How do I write that first if condition? Any help would be appreciated, thank you!
The first thing that comes to mind to check if something is a 'plain object' is to see if it doesn't have a prototype.
So I think I would write this as:
function foo(someVar){
if (typeof "someVar" === 'object' && someVar.prototype === undefined) {
} else if (Array.isArray(someVar)) {
} else {
}
}
This has been answered in separate threads
Date
How to check whether an object is a date?
Array
How to detect if a variable is an array
Object
Check if a value is an object in JavaScript
to check if The value is of type array you could use Array.isArray()
ex:
var first=Array.isArray([1,'fruits',3])
var second=Array.isArray('foo')
console.log(first)
console.log(second)
To check if The value is of type object things get a little bit difficult since type object is an umbrella for too many types like Function,array... but you could use this function, it's by no means is going to work for all types since it doesn't exclude all types but it excludes most of them
ob=[]
function isObject(obj) {
if(obj === Object(obj)){
return Array.isArray(obj)?false:(typeof obj === 'function'?false
:(Object.prototype.toString.call(obj) === '[object Date]'?false
:(Object.prototype.toString.call(obj) === '[object Number]'?false
:(Object.prototype.toString.call(obj) === '[object Math]'?false:true))))}
else
return false
}
console.log(isObject(ob))
To check for type date you could use this
Object.prototype.toString.call(obj) === '[object Date]

Which way should be used for null comparison in JavaScript

To check whether an object is null or not in javascript, i have seen two ways,
let obj = {};
Option 1
if(obj){ console.log('object exists'); }
Option 2
if(!!obj === true){ console.log('object exists'); }
Is there any advantage of choosing one option over the other? which option is preferred as the standard code ?
Use simple ===.
In your code, you are not checking if the object is null or not. Instead, your code just checks if it's not a Falsy value. Note that there are a total of 6 Falsy Values in JavaScript, null being one of those. Read more about them here. If you just want to check if a variable is null, the following is the best way to go:
if (obj === null) {
console.log("object is null");
} else {
console.log("object is not null");
}
If you only want to check if the object is null:
if(obj === null) {
...
}
An object in javascript is always truthy irrespective of whether it has any property defined on it.
Thus,
var obj={}
if(obj){
//this block will always be executed.
}
If you want to check if an object has been defined in the current lexical scope try:
if(typeof(obj) !== 'undefined' && obj !== null){
console.log('object exists');
}
else{
console.log('nope')
}
If you want to check if an object has any property on it or it is an empty object try:
if(typeof(obj) !== 'undefined' && obj !== null){
console.log('object exists');
}
else{
if(Object.keys(obj).length){
//do something
}
}
To test if a value obj is of Object data type, try
if( obj and typeof obj === "object") {
// obj is an object and it's safe to access properties
}
The truthiness of obj indicates it's not null, and typeof returning "object" means it's not some other primitive data type.
Note that null is its own data type in JavaScript (null), but typeof null returns "object" because of limitations in the early implementations of the language.
Conversely, to test if a value is null and not an object, simply use a strict equality test:
if( obj === null) {
// the data type of obj is null
...
}
Choosing a comparison method varies on how you predict how the data type will be,
but if you want a really safe standard, use 'typeof'.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
if( typeof obj === "object" ){ console.log('object exists'); }
if( typeof obj !== "undefined" ){ console.log('object exists'); }

Detect if object is either an Array or typed array

I need to determine whether a given object is either an Array, or typed array such as Float32Array.
Currently I'm checking whether the .length property is defined, but this isn't always indicative of an array. Similar issues arise with existence checking of .forEach() or other methods.
Several instanceof checks would suffice, as done here - but I'm wondering if there is a simple built-in feature, e.g., a generic Array.isArray() function that does what I need.
function isArrayOrTypedArray(x) {
return Boolean(x && (typeof x === 'object') && (Array.isArray(x) || (ArrayBuffer.isView(x) && !(x instanceof DataView)));
}
Unfortunately, I don't believe there is.
You can do the instanceof checks you mentioned, or you could check the result of Object.prototype.toString.call(variable) to see if it's one of the predefind strings ("[object Array]", "[object Uint8Array]", etc.). (Edit: Ah, I see by following the link in your question that that's also demonstrated by that code.)
While I think, as T.J. Crowder already said, there's no built-in function, you should be able to combine Array.isArray and ArrayBuffer.isView to get the functionality you want:
function isArrayOrTypedArray(x) {
return Array.isArray(x) || (ArrayBuffer.isView(x) &&
Object.prototype.toString.call(x) !== "[object DataView]");
}
Array.isArray(x) returns true if x is an array. ArrayBuffer.isView(x)returns true if x is a typed array or DataView, so we just need to ignore the case where x is a DataView to get the function we want.
Demonstration:
function isArrayOrTypedArray(x) {
return Array.isArray(x) || (ArrayBuffer.isView(x) && Object.prototype.toString.call(x) !== "[object DataView]");
}
console.log(isArrayOrTypedArray()); // false
console.log(isArrayOrTypedArray({})); // false
console.log(isArrayOrTypedArray(null)); // false
console.log(isArrayOrTypedArray(undefined)); // false
console.log(isArrayOrTypedArray(new ArrayBuffer(10))); // false
console.log(isArrayOrTypedArray([])); // true
console.log(isArrayOrTypedArray([1,2,3,4,5])); // true
console.log(isArrayOrTypedArray(new Uint8Array())); // true
console.log(isArrayOrTypedArray(new Float32Array())); // true
console.log(isArrayOrTypedArray(new Int8Array(10).subarray(0, 3))); // true
var buffer = new ArrayBuffer(2);
var dv = new DataView(buffer);
console.log(isArrayOrTypedArray(dv)); // false
You could do something like this:
function isArray(array) {
if((array.length || array.length === 0) && (array.constructor !== String)) {
return true;
}
return false;
}
Note that a String also has a length property and we need to exclude that, hence the constructor check.
To determine if x is an ArrayBuffer,
You can take advantage of the fact that new DataView(x) throws "TypeError: First argument to DataView constructor must be an ArrayBuffer" if x isn't an ArrayBuffer.
In other words, simply do:
function isArrayBuffer(x) {
try {
new DataView(x);
return true;
}
catch (TypeError) {
return false;
}
}
And to test if a thing is a TypedArray,
I believe ArrayBuffer.isView does the job.
You can use obj.constructor.name as a way of getting the object's name rather than an instanceof matching ladder.
What all these arrays have in common is they have Array in their classname and a array.length property which is a number.
function isArray(x) {
if (typeof x.length === 'number'
&& x.constructor.name.includes('Array')) {
return true;
}
return false;
}
This works in later versions of Javascript/Node anyway.
You could use name.includes if your JS version supports it.
Array.constructor.name is Array for [] and others like Uint8Array for typed arrays.
In other versions of JavaScript you may need obj.prototype.constructor.name.

constructor vs typeof to detect type in JavaScript

In this question I did not see suggestions to use constructor.
So instead of typeof callback == "function"
I would use callback && (callback.constructor==Function).
To me it seems obvious that comparison to memory pointers is always better than comparison to strings in terms of both runtime performance and coding safety.
Why not use constructor to detect all types and forget about ugly typeof?
It works for all primitive types, functions and arrays:
undefined === undefined
null === null
[1,2,3].constructor == Array
(1).constructor == Number
(true).constructor == Boolean
(()=>null).constructor == Function
'abc'.constructor == String
(new Date()).constructor == Date
else it's an object, where instanceof helps to detect it's parents if needed.
If string interning can be relied upon then runtime performance advantage goes away. But safe coding advantage still stays.
instanceof is better because it works with inherited constructors. .constructor is a mutable property on an object, so it's not a good thing to check because one can simply change it. You can't change the instanceof something.
const x = new Date();
console.log("Date Constructor", x.constructor);
x.constructor = "herpderpderp";
console.log("Date Constructor", x.constructor);
You can also define your own functions for both tests that also work on primitives by using getPrototypeOf and isPrototypeOf. E.G.:
function typeOf(obj) {
return Object.getPrototypeOf(obj).constructor;
}
typeOf(2) === Number // true
typeOf("cats") === String // true
class Foo {}
typeOf(new Foo()) === Foo // true
class Bar extends Foo {}
typeOf(new Bar()) === Bar // true
typeOf(new Bar()) === Foo // false
var b = new Number(3)
if (typeOf(b) === Number) {
console.log(b.valueOf() + 5)
}
and
function instanceOf(obj, type) {
var objType = typeOf(obj)
return (
// Allow native instanceof in case of Symbol.hasInstance
obj instanceof type ||
// Handle case where is of type type
typeOf(obj) === type ||
// Handle general case
type.isPrototypeOf(objType) ||
// Handle special case where type.prototype acts as a
// prototype of the object but its type isn't in the
// prototype chain of the obj's type
// OPTIONALLY remove this case if you don't want
// primitives to be considered instances of Object
type.prototype.isPrototypeOf(objType.prototype)
);
}
instanceOf(3, Number) // true
instanceOf(new Number("2"), Number) // true
instanceOf(2, Number) // true, OPTIONAL with the last condition
// but is probably preferable as 2 does
// indeed get all methods of Objects
class Hat {}
instanceOf(new Hat(), Hat) // true
class Fedora extends Hat {}
instanceOf(new Fedora(), Fedora) // true
instanceOf(new Fedora(), Hat) // true
instanceOf(new Fedora(), Object) // true

How can I differentiate an object literal from other Javascript objects?

Update: I'm rephrasing this question, because the important point to me is identifying the object literal:
How can I tell the difference between an object literal and any other Javascript object (e.g. a DOM node, a Date object, etc.)? How can I write this function:
function f(x) {
if (typeof x === 'object literal')
console.log('Object literal!');
else
console.log('Something else!');
}
So that it only prints Object literal! as a result of the first call below:
f({name: 'Tom'});
f(function() {});
f(new String('howdy'));
f('hello');
f(document);
Original Question
I'm writing a Javascript function that is designed to accept an object literal, a string, or a DOM node as its argument. It needs to handle each argument slightly differently, but at the moment I can't figure out how to differentiate between a DOM node and a plain old object literal.
Here is a greatly simplified version of my function, along with a test for each kind of argument I need to handle:
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);
This code will log the same message for the second two calls. I can't figure out what to include in the else if clause. I've tried other variations like x instanceof Object that have the same effect.
I understand that this might be bad API/code design on my part. Even if it is, I'd still like to know how to do this.
How can I tell the difference between an object literal and any other Javascript object (e.g. a DOM node, a Date object, etc.)?
The short answer is you can't.
An object literal is something like:
var objLiteral = {foo: 'foo', bar: 'bar'};
whereas the same object created using the Object constructor might be:
var obj = new Object();
obj.foo = 'foo';
obj.bar = 'bar';
I don't think there is any reliable way to tell the difference between how the two objects were created.
Why is it important?
A general feature testing strategy is to test the properties of the objects passed to a function to determine if they support the methods that are to be called. That way you don't really care how an object is created.
You can employ "duck typing", but only to a limited extent. You can't guarantee that just because an object has, for example, a getFullYear() method that it is a Date object. Similarly, just because it has a nodeType property doesn't mean it's a 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. That sort of duck typing is extremely simplistic and will fail in some cases.
You may also note that jQuery depends on properties being returned in a specific order - another dangerous assumption that is not supported by any standard (though some supporters are trying to change the standard to suit their assumed behaviour).
Edit 22-Apr-2014: in version 1.10 jQuery includes a support.ownLast property based on testing a single property (apparently this is for IE9 support) to see if inherited properties are enumerated first or last. This continues to ignore the fact that an object's properties can be returned in any order, regardless of whether they are inherited or own, and may be jumbled.
Probably the simplest test for "plain" objects is:
function isPlainObj(o) {
return typeof o == 'object' && o.constructor == Object;
}
Which will always be true for objects created using object literals or the Object constructor, but may well give spurious results for objects created other ways and may (probably will) fail across frames. You could add an instanceof test too, but I can't see that it does anything that the constructor test doesn't.
If you are passing ActiveX objects, best to wrap it in try..catch as they can return all sorts of weird results, even throw errors.
Edit 13-Oct-2015
Of course there are some traps:
isPlainObject( {constructor: 'foo'} ); // false, should be true
// In global scope
var constructor = Object;
isPlainObject( this ); // true, should be false
Messing with the constructor property will cause issues. There are other traps too, such as objects created by constructors other than Object.
Since ES5 is now pretty much ubiquitous, there is Object.getPrototypeOf to check the [[Prototype]] of an object. If it's the buit–in Object.prototype, then the object is a plain object. However, some developers wish to create truly "empty" objects that have no inherited properties. This can be done using:
var emptyObj = Object.create(null);
In this case, the [[Prototype]] property is null. So simply checking if the internal prototype is Object.prototype isn't sufficient.
There is also the reasonably widely used:
Object.prototype.toString.call(valueToTest)
that was specified as returning a string based on the internal [[Class]] property, which for Objects is [object Object]. However, that has changed in ECMAScript 2015 so that tests are performed for other types of object and the default is [object Object], so the object may not be a "plain object", just one that isn't recognised as something else. The specification therefore notes that:
"[testing using toString] does not provide a reliable type testing
mechanism for other kinds of built-in or program defined objects."
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.
Note that there is no way to polyfill getPrototypeOf, so may not be useful if support for older browsers is required (e.g. IE 8 and lower, according to 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>');
});
Similar to #RobG example:
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
}
TEST:
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>');
});
Since all DOM Nodes inherit from the Node interface you could try the following:
if(typeof x === 'string') {
//string
} else if(x instanceof Node) {
//DOM Node
} else {
//everything else
}
But I'm not sure if this works in older versions of Internet Explorer
Move the check for DOM node above the object literal. Check some property that exists on a DOM node to detect a node. I am using the nodeType. It's not very foolproof as you could pass in an object {nodeType: 0 } and that would break this.
if (typeof x == 'string') { /* string */ }
else if ('nodeType' in x) { /* dom node */ }
else if (typeof x == 'object') { /* regular object */ }
All duck typing checks like the one above and even instanceof checks are bound to fail. To truly determine if the given object is actually a DOM node, you need to use something other than the passed-in object itself.
Maybe something like this?
var isPlainObject = function(value){
if(value && value.toString && value.toString() === '[object Object]')
return true;
return false;
};
Or this other approach:
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;
};
If you don't mind using a package i would recommend using lodash for this:
https://lodash.com/docs/4.17.15#isPlainObject

Categories

Resources