Consider the following:
var o1 = {}
var O = function () {
return this
}
var o2 = new O()
var o3 = function() {}
var o4 = [o1, o1]
var output = [
[_.isObject(o1), _.isObject(o2), _.isObject(o3), _.isObject(o4)],
[_.isPlainObject(o1), _.isPlainObject(o2), _.isPlainObject(o3), _.isPlainObject(o4)],
[typeof o1 === 'object', typeof o2 === 'object', typeof o3 === 'object', typeof o4 === 'object'],
[o1 instanceof Array, o2 instanceof Array, o3 instanceof Array, o4 instanceof Array]
]
/* outputs:
[
[true,true,true,true],
[true,false,false,false],
[true,true,false,true],
[false,false,false,true]
]
*/
Clearly we can see that there is a disconnect between .isObject()
:
Which checks if value is the language type of Object. (e.g. arrays, functions, objects, regexes, new Number(0), and new String(' ')
.isPlainObject()
:
Which if value is a plain object, that is, an object created by the Object constructor or one with a Prototype of null
And our good ol' trusty friend typeof x === 'object'
.
I have three questions:
.isObject
and .isPlainObject
behave differently than the native .js type checking?is*
function that behaves exactly the same as typeof x === 'object'
? Obviously I can just continue to use typeof
, but syntactically it's a bit weird to use one or the other in some places, for example the usage of .isObject
will return false positives when checking for typeof x === 'object' && typeof x !== 'function'
. I don't really see any benefit of .isObject
returning true for functions when .isFunction
already exists.
typeof
has nothing to do with whether something is an object. Functions, strings, and {}
have different typeof
and they are all objects. Functions are of course first-class objects, just like strings are first-class objects, therefore isObject
must return true for strings and objects.
For the record the documentation covers this:
Checks if value is the language type of Object. (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))
Wow! that really is a lot to test for without having a handy isObject
method. To be fair most return typeof
as object
, but the point of higher level methods, especially in libraries like lodash, is so the programmer can forget about that nonsense.
If you care about the typeof
an argument, then use typeof
. If you care about objects that are not functions, you have a couple options: you can use typeof
and check strings specially, or you can use isObject && !isFunction
. I would prefer the latter. The latter does happen to say exactly what you are trying to convey so it really is the correct thing to code. If you think when you say "object" that you implicitly don't mean functions, then you do not think functions are first-class objects, or rather you would like your code to more closely resemble a language in which they're not. But then you can't blame lodash for being a library that extensively uses the fact that functions are first-class objects to make a language in which functions are first-class objects more expressive.
I believe that was the bulk of your question. I believe the use case for isPlainObject
is to answer the question "is this just data?" or "is this code?" so objects created as pseudo-classes (new
of something) don't count.
Sometimes code speaks louder than words. Here's the source from lodash:
function isObject(value) {
var type = typeof value;
return !!value && (type == 'object' || type == 'function');
}
function isPlainObject(value) {
var Ctor;
if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value) && !isArguments(value)) ||
(!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) {
return false;
}
var result;
if (lodash.support.ownLast) {
baseForIn(value, function(subValue, key, object) {
result = hasOwnProperty.call(object, key);
return false;
});
return result !== false;
}
baseForIn(value, function(subValue, key) {
result = key;
});
return result === undefined || hasOwnProperty.call(value, result);
}
According to the lodash docs:
Checks if value is the language type of Object. (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))
Checks if value is a plain object, that is, an object created by the Object constructor or one with a [[Prototype]] of null.
Perhaps the existence of an isObject
function is inherently a bit confusing when in JavaScript so many things act like objects. The key idea is that some values in JavaScript are considered primitives and others considered full-blown objects. Strings, numbers, and booleans are treated differently at an internal level than objects created with the object literal or constructor functions (this doesn't end up have much practical significance because primitives will automatically cast themselves to objects when necessary).
The fact that typeof null === 'object'
probably originates from the fact that objects are reference types and null is often returned from functions in lieu of an object. (A quirk which likely hearkens back to the concept of pointers and the NULLPTR in C and C++. The type void *
has many values but the NULLPTR is still considered a valid value for the pointer type.)
isObject
and isPlainObject
would be an extra. They are utility functions. I'm sure they were aware that they are different then typeof which is why they may be useful to some people. They probably thought the performance, consistency, and syntax of the native typeof
didn't warrant the making of .typeOf
that does the same thing..isObject
return true for functions is because functions in JS are objects. It is kind of deceiving when native typeof
returns function and not object. It is further convoluted by the fact that typeof []
doesnt return array and instead returns object so why should it return function and not object on a function. typeof
. Underscore has _.isObject
which is the same as the lodash .isObject
. You could use lodash's .isFunction
and .isObject
to create your own typeof function that returns the same thing native typeof
would.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With