Best way to merge one object into another object (no overwrite) [duplicate] - javascript

This question already has answers here:
How to deeply merge two object values by keys
(5 answers)
Closed 7 years ago.
I want to merge one object (parent) into another object (window) without overwriting existing values.
The keys, values and length are not known of neither objects but i can safely assume that there will be nested objects.
I cannot re-create the target object because of reasons, it needs to be an actual merge.
What is the best way to do this in javascript?
Example:
var target = {
prop1: {
prop1stuff1: 42,
prop3: 15.5
},
42: 'stuff'
};
var source = {
prop1: {
prop1stuff1: 42,
prop4: 17,
prop3: 18
},
'42': 'test'
};
function merge(t, s){
//code to merge source (s) into target (t)
//without overwriting existing values
//and without overwriting t with a new object
}
merge(target, source); //alter target by reference, does not return anything
console.log(target);
// ^ this outputs:
{
prop1: {
prop1stuff1: 42,
prop3: 15.5,
prop4: 17
},
42: 'stuff'
}
Edit:
I can't assign a new object to target, i must add the properties one by one.
I also don't know how deep the nested objects will go
*Second Edit:***
TJ Crowder's answer works but the objects i tried to merge contain a lot of circular references, causing infinite loops.
I added a circular reference detector and i am now going to update TJ Crowder's answer.

You'd do a recursive copy of properties from source to target, with a check to make sure the property doesn't already exist:
function merge(t, s){
// Do nothing if they're the same object
if (t === s) {
return;
}
// Loop through source's own enumerable properties
Object.keys(s).forEach(function(key) {
// Get the value
var val = s[key];
// Is it a non-null object reference?
if (val !== null && typeof val === "object") {
// Yes, if it doesn't exist yet on target, create it
if (!t.hasOwnProperty(key)) {
t[key] = {};
}
// Recurse into that object
merge(t[key], s[key]);
// Not a non-null object ref, copy if target doesn't have it
} else if (!t.hasOwnProperty(key)) {
t[key] = s[key];
}
});
}
Notes:
The above assumes that any object in the source is a plain object, and so if it doesn't exist in the target, we create it using {}. That's not very sophisticated, we might want to go further, such as checking whether it's an array, or other built-in types, and doing something more extensive. But the above should get you started.
We're doing "own" properties above; you could use a for-in loop instead of Object.keys to do properties including ones inherited from prototypes; then you'd use if (!(key in t)) instead of !t.hasOwnProperty(key).
Example:
var common = {
commonProp: "I'm a prop on an object both target and source have"
};
var target = {
prop1: {
prop1stuff1: 42,
prop3: 15.5
},
42: 'stuff',
common: common
};
var source = {
prop1: {
prop1stuff1: 42,
prop4: 17,
prop3: 18
},
'42': 'test',
common: common
};
function merge(t, s){
// Do nothing if they're the same object
if (t === s) {
return;
}
// Loop through source's own enumerable properties
Object.keys(s).forEach(function(key) {
// Get the value
var val = s[key];
// Is it a non-null object reference?
if (val !== null && typeof val === "object") {
// Yes, if it doesn't exist yet on target, create it
if (!t.hasOwnProperty(key)) {
t[key] = {};
}
// Recurse into that object
merge(t[key], s[key]);
// Not a non-null object ref, copy if target doesn't have it
} else if (!t.hasOwnProperty(key)) {
t[key] = s[key];
}
});
}
merge(target, source);
document.body.innerHTML =
"<pre>" + JSON.stringify(target) + "</pre>";
The OP extended the above to handle circular references sufficiently for their purposes (may not be general-purpose):
function merge(t, s){
// Do nothing if they're the same object
if (t === s) return;
// Loop through source's own enumerable properties
Object.keys(s).forEach(function(key) {
// Get the value
var val = s[key];
// Is it a non-null object reference?
if (val !== null && typeof val === "object") {
// Yes, if it doesn't exist yet on target, create it
if (!t.hasOwnProperty(key)) t[key] = {};
// Recurse into that object IF IT DOES NOT CONTAIN CIRCULAR REFERENCES
if ( !isCyclic( t[ key ] ) && !isCyclic( s[ key ] ) ) merge( t[ key ], s[ key ] );
// Not a non-null object ref, copy if target doesn't have it
} else if (!t.hasOwnProperty(key)) t[key] = s[key];
});
function isCyclic( obj ) {
var seenObjects = [];
function detect( obj ) {
if ( obj && typeof obj === 'object' ) {
if ( seenObjects.indexOf( obj ) !== -1 ) return true;
seenObjects.push( obj );
for ( var key in obj ) if ( obj.hasOwnProperty( key ) && detect( obj[ key ] ) ) return true;
}
return false;
}
return detect( obj );
}
//and now... Merge!
merge( window, parent );
//window now has all properties of parent
//that it didn't have before

Related

Is there an easy way to completely freeze object and its children in Javascript, (Deep Freeze)?

Often you have to pass an object as parameter. Functions accessing those objects are pass by reference and can change original object. Which depending on situation can be unwanted outcome. So is there a way to freeze object. I know about Object.freeze()
But it doesn't affect objects/arrays inside it.
For example
a = { arr: [1,2,3]};
Object.freeze(a);
a.arr.push(4); // still works
You can make a very simple recursive solution like so:
let a = {
arr: [1, 2, 3],
name: "A. Bc"
};
const deepFreeze = o => {
for (let [key, value] of Object.entries(o)) {
if (o.hasOwnProperty(key) && typeof value == "object") {
deepFreeze(value);
}
}
Object.freeze(o);
return o;
}
deepFreeze(a);
try {
a.arr.push(4);
} catch(e) {
console.log("Error: ", e);
}
console.log(a);
If you look at MDN, there is a function there that suggests deepFreeze functionality however it is not stack safe. I personally have an ES5 version to async iterate. For ES6 something along these lines might work, I did not test it thoroughly though:
function deepFreeze(o,promises,oldestParent){
promises = promises || [];
oldestParent = oldestParent || o;
promises.push(
Promise.resolve().then(function(){
Object.values(Object.freeze(o)).forEach(function(d,i){
typeof d === "object" && deepFreeze(d,promises,oldestParent);
});
return oldestParent;
})
);
return Promise.all(promises).then((a)=>a[0]);
}
var o = {a:3,b:{test:1,test2:2},c:1};
deepFreeze(o).then(function(x){console.log(x)}); //o is deep frozen
Warning: I assume your object's properties are enumerable, if not then use getOwnPropertyNames instead.
function deepFreeze(object) {
// Retrieve the property names defined on object
var propNames = Object.getOwnPropertyNames(object);
// Freeze properties before freezing self
for (let name of propNames) {
let value = object[name];
object[name] = value && typeof value === "object" ?
deepFreeze(value) : value;
}
return Object.freeze(object);
}
let a = { arr: [1,2,3]};
deepFreeze(a);
a.arr.push(4); // TypeError: Cannot add property 3, object is not extensible
(second code [second gray area]) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze
Thank you :)
To deep-freeze all enumerable properties (ES2015+):
// Recursively freeze an object
const deepFreeze = x => {
Object.freeze(x)
Object.values(x).forEach(deepFreeze)
}
If you have circular references:
// Recursively freeze an object with circular references
const deepFreeze = x => {
Object.freeze(x)
Object.values(x).filter(x => !Object.isFrozen(x)).forEach(deepFreeze)
}
If you also have to deep-freeze any shallow-frozen stuff (slightly slower):
// Recursively deep freeze an object with circular references
const deepFreeze = (x, frozen = []) => {
if (frozen.includes(x)) return null
frozen.push(x)
Object.freeze(x)
Object.values(x).forEach(x => deepFreeze(x, frozen))
}
tldr; Two-liner:
const deepFreeze = (x, frozen = []) => frozen.includes(x) ||
frozen.push(Object.freeze(x)) && Object.values(x).forEach(x => deepFreeze(x, frozen))
Checkout deep-freeze it does recursively Object.freeze() on objects
Here's how they implement it
function deepFreeze (o) {
Object.freeze(o);
Object.getOwnPropertyNames(o).forEach(function (prop) {
if (o.hasOwnProperty(prop)
&& o[prop] !== null
&& (typeof o[prop] === "object" || typeof o[prop] === "function")
&& !Object.isFrozen(o[prop])) {
deepFreeze(o[prop]);
}
});
return o;
};

Create object with sub-objects up to the n-th level [duplicate]

This question already has answers here:
How to set object property (of object property of..) given its string name in JavaScript?
(16 answers)
Closed 6 years ago.
I have an object called MyObject and a method called createEntry() which aims to create a new property for MyObject and sub-properties for the property or just skip it altogether if it already exists.
My code:
var MyObject = {
createEntry: function (val1, val2, val3, val4) {
this[val1] = this[val1] || {};
this[val1][val2] = this[val1][val2] || {};
this[val1][val2][val3] = val4;
}
};
MyObject.createEntry("val1", "val2", "val3", "val4");
As shown in the above function, I'm trying to create a new sub-object for each argument of the method createEntry() except for the last two, where val3 is a property or method and val4 is its value.
With my method in its current state, I can only reach up to level 3 with its subsequent ones requiring longer and longer code. I assume the above can be achieved with a while loop, but I haven't been able to figure it out yet.
Question: How can I create unlimited sub-objects based on the number of arguments passed on the above function in a tree fashion that looks like the following:
var MyObject = {
val1: {
val2 {
val3: val4
}
}
}
reduceRight seems perfect for this:
function createEntry(...args) {
return args.reduceRight(function(prev, curr) {
return {[curr]: prev};
});
}
console.log(createEntry("val1", "val2", "val3", "val4"));
Well, when creating unlimited sub-objects using parameters, I'd iterate each parameter, having the last object I entered as reference. This code is enough to understand.
Note: Object.assign is a new browser implementation, but it has polyfills already. I use this method to concatenate object with object when a entry name already exists in a object
Object['prototype']['createEntries'] = function() {
/* store last object location */
var lastObj = this
/**
* Note: each argument is a object that specify the following properties -->
* inner : the entry object
* *name : the entry property name
*/
for (var i = 0, arg, existent, len = arguments.length; i < len; ++i) {
if (typeof (arg = arguments[i]) === 'object' && (typeof arg.inner === 'object' ? true : arg.inner = {} ))
/* create new entry/keep existent entry and reserve it in lastObj */
lastObj = (typeof (existent = lastObj[arg.name]) === 'object' ?
lastObj[arg.name] = Object.assign(existent, arg.inner) :
lastObj[arg.name] = arg.inner)
}
return this
}
Then this is a basic usage
({}).createEntries({
name: "val1"
}, {
name: "val2"
})
/* {
val1: {
val2: {
}
}
}
*/
I hope this is what you want
Oriol gave a fantastic answer, but his function, due to the way it's built, is not really useful as it returns a new object instead of modifying MyObject.
So, in order to actually modify MyObject, instead of the first return, we have to do something else that will modify MyObject without overwriting it.
To do just that, a function that expands the MyObject must be built:
expand: function (object) {
for (var key in object) if (!!object[key]) {
if (key in this) throw new Error("`" + key + "` already exists.");
else this[key] = object[key];
}
}
Then, all we have to do is to use #Oriol's answer without the first return statement as an argument to expand():
createEntry: function () {
this.expand([].reduceRight.call(arguments, function(previous, current) {
return {[current]: previous};
}));
}
Full code:
var MyObject = {
createEntry: function() {
this.expand([].reduceRight.call(arguments, function(previous, current) {
return {
[current]: previous
};
}));
},
expand: function(object) {
for (var key in object)
if (!!object[key]) {
if (key in this) throw new Error("`" + key + "` already exists.");
else this[key] = object[key];
}
}
};
MyObject.createEntry("val1", "val2", "val3", "val4");
console.log(MyObject);
Thanks to everyone for the help!

How to set class name for JavaScript object

I have some test class
TestKlass = (function() {
function TestKlass(value) {
this.value = value != null ? value : 'hello world';
console.log(this.value);
}
return TestKlass;
})();
x = new TestKlass;
x instanceof TestKlass; (gives true)
I have new empty object
y = {}
y instanceof Object
Can I find any ways to set any properties for y, something like this
y.__proto__ = x.__proto__
y.constructor.prototype = x.constructor.prototype
for to have this result
y instanceof TestKlass => true
====================================================
UPD:
So. My main aim - it's to create CLONE function. Now my solution works for me. Please look at this code:
JavaScript JS object clone
Object._clone = function(obj) {
var clone, property, value;
if (!obj || typeof obj !== 'object') {
return obj;
}
clone = typeof obj.pop === 'function' ? [] : {};
clone.__proto__ = obj.__proto__;
for (property in obj) {
if (obj.hasOwnProperty(property)) {
value = obj.property;
if (value && typeof value === 'object') {
clone[property] = Object._clone(value);
} else {
clone[property] = obj[property];
}
}
}
return clone;
};
CoffeeScript JS object clone
# Object clone
Object._clone = (obj) ->
return obj if not obj or typeof(obj) isnt 'object'
clone = if typeof(obj.pop) is 'function' then [] else {}
# deprecated, but need for instanceof method
clone.__proto__ = obj.__proto__
for property of obj
if obj.hasOwnProperty property
# clone properties
value = obj.property
if value and typeof(value) is 'object'
clone[property] = Object._clone(value)
else
clone[property] = obj[property]
clone
Now you can try to do that
A = new TestKlass
B = Object._clone(A)
B instanceof TestKlass => true
It's work fine with Moz FF 13. But I think it's not cross-browser. and proto is deprecated.
I think there is no universal solution. But maybe It's will be helpful for somebody.
Perhaps you should read the following answer first. What you are trying to achieve is really simple. However before I explain let's look at your solutions:
Solution 1
y.__proto__ = x.__proto__;
I wouldn't recommend doing this as the __proto__ property is now deprecated. The above code won't work in Rhino.
Solution 2
y.constructor.prototype = x.constructor.prototype;
This is a very bad solution. The reason is that y.constructor is actually y.__proto__.constructor and since y is an object y.__proto__ is the same as Object.prototype. This means that y.constructor is the same as Object and you're essentially changing the prototype property of Object. This could potentially break other code on your page. I strongly discourage this practice.
Solution 3 (my solution)
What you want to do is simply change the internal [[proto]] property of an object. Since there's no cross platform way to do so we need to employ a hack. A simple solution would be to create a new object with the desired __proto__ property and set getters and setters on it to change the original object. It would be as follows:
function setPrototypeOf(object, prototype) {
var constructor = function () {
for (var key in object) {
if (object.hasOwnProperty(key)) {
(function (key) {
Object.defineProperty(this, key, {
get: function () {
return object[key];
},
set: function (value) {
object[key] = value;
},
enumerable: true
});
}).call(this, key);
}
}
};
constructor.prototype = prototype;
return new constructor;
}
After this all you need to do is:
y = setPrototypeOf(y, TestKlass.prototype);
Here is a working fiddle.
Edit:
The problem with your clone function is that it only clones objects and arrays. You forgot to account for functions which are also passed by reference. Thus when you clone an object which has methods which close over the internal state of the object, the object and its clone will share the same internal state. See the following fiddle:
function Value(value) {
this.get = function () {
alert(value);
};
this.set = function (newValue) {
value = newValue;
};
}
var a = new Value(5);
var b = Object._clone(a);
b.set(10); // setting b to 10 also sets a to 10
a.get(); // alerts 10, should alert 5
There's absolutely no way in JavaScript to clone the internal state of an object so cloning objects and arrays in JavaScript will only work for objects which do not expose closures as methods.
To know more about the problems with cloning objects in JavaScript read this answer. Click on the following link to read John Resig's answer.
obj instanceof SomeConstructor checks whether SomeConstructor is found anywhere in the prototype chain of obj.
If you want inheritance, you need to set the .prototype of FirstConstructor to a newly created SomeConstructor object.
SomeConstructor = function() {}
FirstConstructor = function() {}
FirstConstroctor.prototype = new SomeConstructor();
var y = new FirstConstroctor();
y instanceof FirstConstructor; // true
y instanceof SomeConstructor ; // true, it bubbles from FirstConstructor.__proto__ to SomeConstructor

Stringify (convert to JSON) a JavaScript object with circular reference

I've got a JavaScript object definition which contains a circular reference: it has a property that references the parent object.
It also has functions that I don't want to be passed through to the server. How would I serialize and deserialize these objects?
I've read that the best method to do this is to use Douglas Crockford's stringify. However, I'm getting the following error in Chrome:
TypeError: Converting circular structure to JSON
The code:
function finger(xid, xparent){
this.id = xid;
this.xparent;
//other attributes
}
function arm(xid, xparent){
this.id = xid;
this.parent = xparent;
this.fingers = [];
//other attributes
this.moveArm = function() {
//moveArm function details - not included in this testcase
alert("moveArm Executed");
}
}
function person(xid, xparent, xname){
this.id = xid;
this.parent = xparent;
this.name = xname
this.arms = []
this.createArms = function () {
this.arms[this.arms.length] = new arm(this.id, this);
}
}
function group(xid, xparent){
this.id = xid;
this.parent = xparent;
this.people = [];
that = this;
this.createPerson = function () {
this.people[this.people.length] = new person(this.people.length, this, "someName");
//other commands
}
this.saveGroup = function () {
alert(JSON.stringify(that.people));
}
}
This is a test case that I created for this question. There are errors within this code but essentially I have objects within objects, and a reference passed to each object to show what the parent object is when the object is created. Each object also contains functions, which I don't want stringified. I just want the properties such as the Person.Name.
How do I serialize before sending to the server and deserialize it assuming that the same JSON is passed back?
Circular structure error occurs when you have a property of the object which is the object itself directly (a -> a) or indirectly (a -> b -> a).
To avoid the error message, tell JSON.stringify what to do when it encounters a circular reference.
For example, if you have a person pointing to another person ("parent"), which may (or may not) point to the original person, do the following:
JSON.stringify( that.person, function( key, value) {
if( key == 'parent') { return value.id;}
else {return value;}
})
The second parameter to stringify is a filter function. Here it simply converts the referred object to its ID, but you are free to do whatever you like to break the circular reference.
You can test the above code with the following:
function Person( params) {
this.id = params['id'];
this.name = params['name'];
this.father = null;
this.fingers = [];
// etc.
}
var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him;
JSON.stringify(me); // so far so good
him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
if(key == 'father') {
return value.id;
} else {
return value;
};
});
BTW, I'd choose a different attribute name to "parent" since it is a reserved word in many languages (and in DOM). This tends to cause confusion down the road...
No-lib
Use below replacer to generate json with string references (similar to json-path) to duplicate/circular referenced objects
let s = JSON.stringify(obj, refReplacer());
function refReplacer() {
let m = new Map(), v= new Map(), init = null;
return function(field, value) {
let p= m.get(this) + (Array.isArray(this) ? `[${field}]` : '.' + field);
let isComplex= value===Object(value)
if (isComplex) m.set(value, p);
let pp = v.get(value)||'';
let path = p.replace(/undefined\.\.?/,'');
let val = pp ? `#REF:${pp[0]=='[' ? '$':'$.'}${pp}` : value;
!init ? (init=value) : (val===init ? val="#REF:$" : 0);
if(!pp && isComplex) v.set(value, path);
return val;
}
}
// ---------------
// TEST
// ---------------
// gen obj with duplicate references
let a = { a1: 1, a2: 2 };
let b = { b1: 3, b2: "4" };
let obj = { o1: { o2: a }, b, a }; // duplicate reference
a.a3 = [1,2,b]; // circular reference
b.b3 = a; // circular reference
let s = JSON.stringify(obj, refReplacer(), 4);
console.log(s);
And following parser function to regenerate object from such "ref-json"
function parseRefJSON(json) {
let objToPath = new Map();
let pathToObj = new Map();
let o = JSON.parse(json);
let traverse = (parent, field) => {
let obj = parent;
let path = '#REF:$';
if (field !== undefined) {
obj = parent[field];
path = objToPath.get(parent) + (Array.isArray(parent) ? `[${field}]` : `${field?'.'+field:''}`);
}
objToPath.set(obj, path);
pathToObj.set(path, obj);
let ref = pathToObj.get(obj);
if (ref) parent[field] = ref;
for (let f in obj) if (obj === Object(obj)) traverse(obj, f);
}
traverse(o);
return o;
}
// ------------
// TEST
// ------------
let s = `{
"o1": {
"o2": {
"a1": 1,
"a2": 2,
"a3": [
1,
2,
{
"b1": 3,
"b2": "4",
"b3": "#REF:$.o1.o2"
}
]
}
},
"b": "#REF:$.o1.o2.a3[2]",
"a": "#REF:$.o1.o2"
}`;
console.log('Open Chrome console to see nested fields:');
let obj = parseRefJSON(s);
console.log(obj);
It appears that dojo can represent circular references in JSON in the form : {"id":"1","me":{"$ref":"1"}}
Here is an example:
http://jsfiddle.net/dumeG/
require(["dojox/json/ref"], function(){
var me = {
name:"Kris",
father:{name:"Bill"},
mother:{name:"Karen"}
};
me.father.wife = me.mother;
var jsonMe = dojox.json.ref.toJson(me); // serialize me
alert(jsonMe);
});​
Produces:
{
"name":"Kris",
"father":{
"name":"Bill",
"wife":{
"name":"Karen"
}
},
"mother":{
"$ref":"#father.wife"
}
}
Note: You can also de-serialize these circular referenced objects using the dojox.json.ref.fromJson method.
Other Resources:
How to serialize DOM node to JSON even if there are circular references?
JSON.stringify can't represent circular references
I found two suitable modules to handle circular references in JSON.
CircularJSON https://github.com/WebReflection/circular-json whose output can be used as input to .parse(). It also works in Browsers & Node.js Also see: http://webreflection.blogspot.com.au/2013/03/solving-cycles-recursions-and-circulars.html
Isaacs json-stringify-safe https://github.com/isaacs/json-stringify-safe which maybe more readable but can't be used for .parse and is only available for Node.js
Either of these should meet your needs.
Happened upon this thread because I needed to log complex objects to a page, since remote debugging wasn't possible in my particular situation. Found Douglas Crockford's (inceptor of JSON) own cycle.js, which annotates circular references as strings such that they can be reconnected after parsing. The de-cycled deep copy is safe to pass through JSON.stringify. Enjoy!
https://github.com/douglascrockford/JSON-js
cycle.js: This file contains two functions, JSON.decycle and
JSON.retrocycle, which make it possible to encode cyclical structures
and dags in JSON, and to then recover them. This is a capability that
is not provided by ES5. JSONPath is used to represent the links.
I used the following to eliminate the circular references:
JS.dropClasses = function(o) {
for (var p in o) {
if (o[p] instanceof jQuery || o[p] instanceof HTMLElement) {
o[p] = null;
}
else if (typeof o[p] == 'object' )
JS.dropClasses(o[p]);
}
};
JSON.stringify(JS.dropClasses(e));

Get the name of an object's type

Is there a JavaScript equivalent of Java's class.getName()?
Is there a JavaScript equivalent of Java's class.getName()?
No.
ES2015 Update: the name of class Foo {} is Foo.name. The name of thing's class, regardless of thing's type, is thing.constructor.name. Builtin constructors in an ES2015 environment have the correct name property; for instance (2).constructor.name is "Number".
But here are various hacks that all fall down in one way or another:
Here is a hack that will do what you need - be aware that it modifies the Object's prototype, something people frown upon (usually for good reason)
Object.prototype.getName = function() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
};
Now, all of your objects will have the function, getName(), that will return the name of the constructor as a string. I have tested this in FF3 and IE7, I can't speak for other implementations.
If you don't want to do that, here is a discussion on the various ways of determining types in JavaScript...
I recently updated this to be a bit more exhaustive, though it is hardly that. Corrections welcome...
Using the constructor property...
Every object has a value for its constructor property, but depending on how that object was constructed as well as what you want to do with that value, it may or may not be useful.
Generally speaking, you can use the constructor property to test the type of the object like so:
var myArray = [1,2,3];
(myArray.constructor == Array); // true
So, that works well enough for most needs. That said...
Caveats
Will not work AT ALL in many cases
This pattern, though broken, is quite common:
function Thingy() {
}
Thingy.prototype = {
method1: function() {
},
method2: function() {
}
};
Objects constructed via new Thingy will have a constructor property that points to Object, not Thingy. So we fall right at the outset; you simply cannot trust constructor in a codebase that you don't control.
Multiple Inheritance
An example where it isn't as obvious is using multiple inheritance:
function a() { this.foo = 1;}
function b() { this.bar = 2; }
b.prototype = new a(); // b inherits from a
Things now don't work as you might expect them to:
var f = new b(); // instantiate a new object with the b constructor
(f.constructor == b); // false
(f.constructor == a); // true
So, you might get unexpected results if the object your testing has a different object set as its prototype. There are ways around this outside the scope of this discussion.
There are other uses for the constructor property, some of them interesting, others not so much; for now we will not delve into those uses since it isn't relevant to this discussion.
Will not work cross-frame and cross-window
Using .constructor for type checking will break when you want to check the type of objects coming from different window objects, say that of an iframe or a popup window. This is because there's a different version of each core type constructor in each `window', i.e.
iframe.contentWindow.Array === Array // false
Using the instanceof operator...
The instanceof operator is a clean way of testing object type as well, but has its own potential issues, just like the constructor property.
var myArray = [1,2,3];
(myArray instanceof Array); // true
(myArray instanceof Object); // true
But instanceof fails to work for literal values (because literals are not Objects)
3 instanceof Number // false
'abc' instanceof String // false
true instanceof Boolean // false
The literals need to be wrapped in an Object in order for instanceof to work, for example
new Number(3) instanceof Number // true
The .constructor check works fine for literals because the . method invocation implicitly wraps the literals in their respective object type
3..constructor === Number // true
'abc'.constructor === String // true
true.constructor === Boolean // true
Why two dots for the 3? Because Javascript interprets the first dot as a decimal point ;)
Will not work cross-frame and cross-window
instanceof also will not work across different windows, for the same reason as the constructor property check.
Using the name property of the constructor property...
Does not work AT ALL in many cases
Again, see above; it's quite common for constructor to be utterly and completely wrong and useless.
Does NOT work in <IE9
Using myObjectInstance.constructor.name will give you a string containing the name of the constructor function used, but is subject to the caveats about the constructor property that were mentioned earlier.
For IE9 and above, you can monkey-patch in support:
if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
var funcNameRegex = /function\s+([^\s(]+)\s*\(/;
var results = (funcNameRegex).exec((this).toString());
return (results && results.length > 1) ? results[1] : "";
},
set: function(value) {}
});
}
Updated version from the article in question. This was added 3 months after the article was published, this is the recommended version to use by the article's author Matthew Scharley. This change was inspired by comments pointing out potential pitfalls in the previous code.
if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
var funcNameRegex = /function\s([^(]{1,})\(/;
var results = (funcNameRegex).exec((this).toString());
return (results && results.length > 1) ? results[1].trim() : "";
},
set: function(value) {}
});
}
Using Object.prototype.toString
It turns out, as this post details, you can use Object.prototype.toString - the low level and generic implementation of toString - to get the type for all built-in types
Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]
One could write a short helper function such as
function type(obj){
return Object.prototype.toString.call(obj).slice(8, -1);
}
to remove the cruft and get at just the type name
type('abc') // String
However, it will return Object for all user-defined types.
Caveats for all...
All of these are subject to one potential problem, and that is the question of how the object in question was constructed. Here are various ways of building objects and the values that the different methods of type checking will return:
// using a named function:
function Foo() { this.a = 1; }
var obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // true
(obj.constructor.name == "Foo"); // true
// let's add some prototypical inheritance
function Bar() { this.b = 2; }
Foo.prototype = new Bar();
obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // false
(obj.constructor.name == "Foo"); // false
// using an anonymous function:
obj = new (function() { this.a = 1; })();
(obj instanceof Object); // true
(obj.constructor == obj.constructor); // true
(obj.constructor.name == ""); // true
// using an anonymous function assigned to a variable
var Foo = function() { this.a = 1; };
obj = new Foo();
(obj instanceof Object); // true
(obj instanceof Foo); // true
(obj.constructor == Foo); // true
(obj.constructor.name == ""); // true
// using object literal syntax
obj = { foo : 1 };
(obj instanceof Object); // true
(obj.constructor == Object); // true
(obj.constructor.name == "Object"); // true
While not all permutations are present in this set of examples, hopefully there are enough to provide you with an idea about how messy things might get depending on your needs. Don't assume anything, if you don't understand exactly what you are after, you may end up with code breaking where you don't expect it to because of a lack of grokking the subtleties.
NOTE:
Discussion of the typeof operator may appear to be a glaring omission, but it really isn't useful in helping to identify whether an object is a given type, since it is very simplistic. Understanding where typeof is useful is important, but I don't currently feel that it is terribly relevant to this discussion. My mind is open to change though. :)
Jason Bunting's answer gave me enough of a clue to find what I needed:
<<Object instance>>.constructor.name
So, for example, in the following piece of code:
function MyObject() {}
var myInstance = new MyObject();
myInstance.constructor.name would return "MyObject".
A little trick I use:
function Square(){
this.className = "Square";
this.corners = 4;
}
var MySquare = new Square();
console.log(MySquare.className); // "Square"
Update
To be precise, I think OP asked for a function that retrieves the constructor name for a particular object. In terms of Javascript, object does not have a type but is a type of and in itself. However, different objects can have different constructors.
Object.prototype.getConstructorName = function () {
var str = (this.prototype ? this.prototype.constructor : this.constructor).toString();
var cname = str.match(/function\s(\w*)/)[1];
var aliases = ["", "anonymous", "Anonymous"];
return aliases.indexOf(cname) > -1 ? "Function" : cname;
}
new Array().getConstructorName(); // returns "Array"
(function () {})().getConstructorName(); // returns "Function"
 
Note: the below example is deprecated.
A blog post linked by Christian Sciberras contains a good example on how to do it. Namely, by extending the Object prototype:
if (!Object.prototype.getClassName) {
Object.prototype.getClassName = function () {
return Object.prototype.toString.call(this).match(/^\[object\s(.*)\]$/)[1];
}
}
var test = [1,2,3,4,5];
alert(test.getClassName()); // returns Array
Using Object.prototype.toString
It turns out, as this post details, you can use Object.prototype.toString - the low level and generic implementation of toString - to get the type for all built-in types
Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]
One could write a short helper function such as
function type(obj){
return Object.prototype.toString.call(obj]).match(/\s\w+/)[0].trim()
}
return [object String] as String
return [object Number] as Number
return [object Object] as Object
return [object Undefined] as Undefined
return [object Function] as Function
You should use somevar.constructor.name like a:
const getVariableType = a => a.constructor.name.toLowerCase();
const d = new Date();
const res1 = getVariableType(d); // 'date'
const num = 5;
const res2 = getVariableType(num); // 'number'
const fn = () => {};
const res3 = getVariableType(fn); // 'function'
console.log(res1); // 'date'
console.log(res2); // 'number'
console.log(res3); // 'function'
Here is a solution that I have come up with that solves the shortcomings of instanceof. It can check an object's types from cross-windows and cross-frames and doesn't have problems with primitive types.
function getType(o) {
return Object.prototype.toString.call(o).match(/^\[object\s(.*)\]$/)[1];
}
function isInstance(obj, type) {
var ret = false,
isTypeAString = getType(type) == "String",
functionConstructor, i, l, typeArray, context;
if (!isTypeAString && getType(type) != "Function") {
throw new TypeError("type argument must be a string or function");
}
if (obj !== undefined && obj !== null && obj.constructor) {
//get the Function constructor
functionConstructor = obj.constructor;
while (functionConstructor != functionConstructor.constructor) {
functionConstructor = functionConstructor.constructor;
}
//get the object's window
context = functionConstructor == Function ? self : functionConstructor("return window")();
//get the constructor for the type
if (isTypeAString) {
//type is a string so we'll build the context (window.Array or window.some.Type)
for (typeArray = type.split("."), i = 0, l = typeArray.length; i < l && context; i++) {
context = context[typeArray[i]];
}
} else {
//type is a function so execute the function passing in the object's window
//the return should be a constructor
context = type(context);
}
//check if the object is an instance of the constructor
if (context) {
ret = obj instanceof context;
if (!ret && (type == "Number" || type == "String" || type == "Boolean")) {
ret = obj.constructor == context
}
}
}
return ret;
}
isInstance requires two parameters: an object and a type. The real trick to how it works is that it checks if the object is from the same window and if not gets the object's window.
Examples:
isInstance([], "Array"); //true
isInstance("some string", "String"); //true
isInstance(new Object(), "Object"); //true
function Animal() {}
function Dog() {}
Dog.prototype = new Animal();
isInstance(new Dog(), "Dog"); //true
isInstance(new Dog(), "Animal"); //true
isInstance(new Dog(), "Object"); //true
isInstance(new Animal(), "Dog"); //false
The type argument can also be a callback function which returns a constructor. The callback function will receive one parameter which is the window of the provided object.
Examples:
//"Arguments" type check
var args = (function() {
return arguments;
}());
isInstance(args, function(w) {
return w.Function("return arguments.constructor")();
}); //true
//"NodeList" type check
var nl = document.getElementsByTagName("*");
isInstance(nl, function(w) {
return w.document.getElementsByTagName("bs").constructor;
}); //true
One thing to keep in mind is that IE < 9 does not provide the constructor on all objects so the above test for NodeList would return false and also a isInstance(alert, "Function") would return false.
I was actually looking for a similar thing and came across this question. Here is how I get types: jsfiddle
var TypeOf = function ( thing ) {
var typeOfThing = typeof thing;
if ( 'object' === typeOfThing ) {
typeOfThing = Object.prototype.toString.call( thing );
if ( '[object Object]' === typeOfThing ) {
if ( thing.constructor.name ) {
return thing.constructor.name;
}
else if ( '[' === thing.constructor.toString().charAt(0) ) {
typeOfThing = typeOfThing.substring( 8,typeOfThing.length - 1 );
}
else {
typeOfThing = thing.constructor.toString().match( /function\s*(\w+)/ );
if ( typeOfThing ) {
return typeOfThing[1];
}
else {
return 'Function';
}
}
}
else {
typeOfThing = typeOfThing.substring( 8,typeOfThing.length - 1 );
}
}
return typeOfThing.charAt(0).toUpperCase() + typeOfThing.slice(1);
}
Use constructor.name when you can, and regex function when I can't.
Function.prototype.getName = function(){
if (typeof this.name != 'undefined')
return this.name;
else
return /function (.+)\(/.exec(this.toString())[1];
};
The kind() function from Agave.JS will return:
the closest prototype in the inheritance tree
for always-primitive types like 'null' and 'undefined', the primitive name.
It works on all JS objects and primitives, regardless of how they were created, and doesn't have any surprises.
var kind = function(item) {
var getPrototype = function(item) {
return Object.prototype.toString.call(item).slice(8, -1);
};
var kind, Undefined;
if (item === null ) {
kind = 'null';
} else {
if ( item === Undefined ) {
kind = 'undefined';
} else {
var prototype = getPrototype(item);
if ( ( prototype === 'Number' ) && isNaN(item) ) {
kind = 'NaN';
} else {
kind = prototype;
}
}
}
return kind;
};
Examples:
Numbers
kind(37) === 'Number'
kind(3.14) === 'Number'
kind(Math.LN2) === 'Number'
kind(Infinity) === 'Number'
kind(Number(1)) === 'Number'
kind(new Number(1)) === 'Number'
NaN
kind(NaN) === 'NaN'
Strings
kind('') === 'String'
kind('bla') === 'String'
kind(String("abc")) === 'String'
kind(new String("abc")) === 'String'
Booleans
kind(true) === 'Boolean'
kind(false) === 'Boolean'
kind(new Boolean(true)) === 'Boolean'
Arrays
kind([1, 2, 4]) === 'Array'
kind(new Array(1, 2, 3)) === 'Array'
Objects
kind({a:1}) === 'Object'
kind(new Object()) === 'Object'
Dates
kind(new Date()) === 'Date'
Functions
kind(function(){}) === 'Function'
kind(new Function("console.log(arguments)")) === 'Function'
kind(Math.sin) === 'Function'
undefined
kind(undefined) === 'undefined'
null
kind(null) === 'null'
Here is an implementation based on the accepted answer:
/**
* Describes the type of a variable.
*/
class VariableType
{
type;
name;
/**
* Creates a new VariableType.
*
* #param {"undefined" | "null" | "boolean" | "number" | "bigint" | "array" | "string" | "symbol" |
* "function" | "class" | "object"} type the name of the type
* #param {null | string} [name = null] the name of the type (the function or class name)
* #throws {RangeError} if neither <code>type</code> or <code>name</code> are set. If <code>type</code>
* does not have a name (e.g. "number" or "array") but <code>name</code> is set.
*/
constructor(type, name = null)
{
switch (type)
{
case "undefined":
case "null":
case "boolean" :
case "number" :
case "bigint":
case "array":
case "string":
case "symbol":
if (name !== null)
throw new RangeError(type + " may not have a name");
}
this.type = type;
this.name = name;
}
/**
* #return {string} the string representation of this object
*/
toString()
{
let result;
switch (this.type)
{
case "function":
case "class":
{
result = "a ";
break;
}
case "object":
{
result = "an ";
break;
}
default:
return this.type;
}
result += this.type;
if (this.name !== null)
result += " named " + this.name;
return result;
}
}
const functionNamePattern = /^function\s+([^(]+)?\(/;
const classNamePattern = /^class(\s+[^{]+)?{/;
/**
* Returns the type information of a value.
*
* <ul>
* <li>If the input is undefined, returns <code>(type="undefined", name=null)</code>.</li>
* <li>If the input is null, returns <code>(type="null", name=null)</code>.</li>
* <li>If the input is a primitive boolean, returns <code>(type="boolean", name=null)</code>.</li>
* <li>If the input is a primitive number, returns <code>(type="number", name=null)</code>.</li>
* <li>If the input is a primitive or wrapper bigint, returns
* <code>(type="bigint", name=null)</code>.</li>
* <li>If the input is an array, returns <code>(type="array", name=null)</code>.</li>
* <li>If the input is a primitive string, returns <code>(type="string", name=null)</code>.</li>
* <li>If the input is a primitive symbol, returns <code>(type="symbol", null)</code>.</li>
* <li>If the input is a function, returns <code>(type="function", name=the function name)</code>. If the
* input is an arrow or anonymous function, its name is <code>null</code>.</li>
* <li>If the input is a function, returns <code>(type="function", name=the function name)</code>.</li>
* <li>If the input is a class, returns <code>(type="class", name=the name of the class)</code>.
* <li>If the input is an object, returns
* <code>(type="object", name=the name of the object's class)</code>.
* </li>
* </ul>
*
* Please note that built-in types (such as <code>Object</code>, <code>String</code> or <code>Number</code>)
* may return type <code>function</code> instead of <code>class</code>.
*
* #param {object} value a value
* #return {VariableType} <code>value</code>'s type
* #see http://stackoverflow.com/a/332429/14731
* #see isPrimitive
*/
function getTypeInfo(value)
{
if (value === null)
return new VariableType("null");
const typeOfValue = typeof (value);
const isPrimitive = typeOfValue !== "function" && typeOfValue !== "object";
if (isPrimitive)
return new VariableType(typeOfValue);
const objectToString = Object.prototype.toString.call(value).slice(8, -1);
// eslint-disable-next-line #typescript-eslint/ban-types
const valueToString = value.toString();
if (objectToString === "Function")
{
// A function or a constructor
const indexOfArrow = valueToString.indexOf("=>");
const indexOfBody = valueToString.indexOf("{");
if (indexOfArrow !== -1 && (indexOfBody === -1 || indexOfArrow < indexOfBody))
{
// Arrow function
return new VariableType("function");
}
// Anonymous and named functions
const functionName = functionNamePattern.exec(valueToString);
if (functionName !== null && typeof (functionName[1]) !== "undefined")
{
// Found a named function or class constructor
return new VariableType("function", functionName[1].trim());
}
const className = classNamePattern.exec(valueToString);
if (className !== null && typeof (className[1]) !== "undefined")
{
// When running under ES6+
return new VariableType("class", className[1].trim());
}
// Anonymous function
return new VariableType("function");
}
if (objectToString === "Array")
return new VariableType("array");
const classInfo = getTypeInfo(value.constructor);
return new VariableType("object", classInfo.name);
}
function UserFunction()
{
}
function UserClass()
{
}
let anonymousFunction = function()
{
};
let arrowFunction = i => i + 1;
console.log("getTypeInfo(undefined): " + getTypeInfo(undefined));
console.log("getTypeInfo(null): " + getTypeInfo(null));
console.log("getTypeInfo(true): " + getTypeInfo(true));
console.log("getTypeInfo(5): " + getTypeInfo(5));
console.log("getTypeInfo(\"text\"): " + getTypeInfo("text"));
console.log("getTypeInfo(userFunction): " + getTypeInfo(UserFunction));
console.log("getTypeInfo(anonymousFunction): " + getTypeInfo(anonymousFunction));
console.log("getTypeInfo(arrowFunction): " + getTypeInfo(arrowFunction));
console.log("getTypeInfo(userObject): " + getTypeInfo(new UserClass()));
console.log("getTypeInfo(nativeObject): " + getTypeInfo(navigator.mediaDevices.getUserMedia));
We only use the constructor property when we have no other choice.
You can use the instanceof operator to see if an object is an instance of another, but since there are no classes, you can't get a class name.
You can use the "instanceof" operator to determine if an object is an instance of a certain class or not. If you do not know the name of an object's type, you can use its constructor property. The constructor property of objects, is a reference to the function that is used to initialize them. Example:
function Circle (x,y,radius) {
this._x = x;
this._y = y;
this._radius = raduius;
}
var c1 = new Circle(10,20,5);
Now c1.constructor is a reference to the Circle() function.
You can alsow use the typeof operator, but the typeof operator shows limited information. One solution is to use the toString() method of the Object global object. For example if you have an object, say myObject, you can use the toString() method of the global Object to determine the type of the class of myObject. Use this:
Object.prototype.toString.apply(myObject);
Say you have var obj;
If you just want the name of obj's type, like "Object", "Array", or "String",
you can use this:
Object.prototype.toString.call(obj).split(' ')[1].replace(']', '');
The closest you can get is typeof, but it only returns "object" for any sort of custom type. For those, see Jason Bunting.
Edit, Jason's deleted his post for some reason, so just use Object's constructor property.
For of those of you reading this and want a simple solution that works fairly well and has been tested:
const getTypeName = (thing) => {
const name = typeof thing
if (name !== 'object') return name
if (thing instanceof Error) return 'error'
if (!thing) return 'null'
return ({}).toString.call(thing).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
To get insight on why this works, checkout the polyfill documentation for Array.isArray(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#polyfill
If anyone was looking for a solution which is working with jQuery, here is the adjusted wiki code (the original breaks jQuery).
Object.defineProperty(Object.prototype, "getClassName", {
value: function() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
}
});
Lodash has many isMethods so if you're using Lodash maybe a mixin like this can be useful:
// Mixin for identifying a Javascript Object
_.mixin({
'identify' : function(object) {
var output;
var isMethods = ['isArguments', 'isArray', 'isArguments', 'isBoolean', 'isDate', 'isArguments',
'isElement', 'isError', 'isFunction', 'isNaN', 'isNull', 'isNumber',
'isPlainObject', 'isRegExp', 'isString', 'isTypedArray', 'isUndefined', 'isEmpty', 'isObject']
this.each(isMethods, function (method) {
if (this[method](object)) {
output = method;
return false;
}
}.bind(this));
return output;
}
});
It adds a method to lodash called "identify" which works as follow:
console.log(_.identify('hello friend')); // isString
Plunker:
http://plnkr.co/edit/Zdr0KDtQt76Ul3KTEDSN
Ok, folks I've been slowly building a catch all method for this over some years lol! The trick is to:
Have a mechanism for creating classes.
Have a mechanism for checking all user created classes, primitives and values created/generated by native constructors.
Have a mechanism for extending user created classes into new ones so that the above functionality permeates through your code/application/library/etc..
For an example (or to see how I dealt with the problem) look at the following code on github: https://github.com/elycruz/sjljs/blob/master/src/sjl/sjl.js and search for:
classOf =,
classOfIs =, and or
defineSubClass = (without the backticks (`)).
As you can see I have some mechanisms in place to force classOf to always give me the classes/constructors type name regardless of whether it is a primitive, a user defined class, a value created using a native constructor, Null, NaN, etc.. For every single javascript value I will get it's unique type name from the classOf function. In addition I can pass in actual constructors into sjl.classOfIs to check a value's type in addition to being able to pass in it's type name as well! So for example:
```
// Please forgive long namespaces! I had no idea on the impact until after using them for a while (they suck haha)
var SomeCustomClass = sjl.package.stdlib.Extendable.extend({
constructor: function SomeCustomClass () {},
// ...
}),
HelloIterator = sjl.ns.stdlib.Iterator.extend(
function HelloIterator () {},
{ /* ... methods here ... */ },
{ /* ... static props/methods here ... */ }
),
helloIt = new HelloIterator();
sjl.classOfIs(new SomeCustomClass(), SomeCustomClass) === true; // `true`
sjl.classOfIs(helloIt, HelloIterator) === true; // `true`
var someString = 'helloworld';
sjl.classOfIs(someString, String) === true; // `true`
sjl.classOfIs(99, Number) === true; // true
sjl.classOf(NaN) === 'NaN'; // true
sjl.classOf(new Map()) === 'Map';
sjl.classOf(new Set()) === 'Set';
sjl.classOfIs([1, 2, 4], Array) === true; // `true`
// etc..
// Also optionally the type you want to check against could be the type's name
sjl.classOfIs(['a', 'b', 'c'], 'Array') === true; // `true`!
sjl.classOfIs(helloIt, 'HelloIterator') === true; // `true`!
```
If you are interested in reading more on how I use the setup mentioned above take a look at the repo: https://github.com/elycruz/sjljs
Also books with content on the subject:
- "JavaScript Patterns" by Stoyan Stefanov.
- "Javascript - The Definitive Guide." by David Flanagan.
- and many others.. (search le` web).
Also you can quickly test the features I'm talking about here:
- http://sjljs.elycruz.com/0.5.18/tests/for-browser/ (also the 0.5.18 path in the url has the sources from github on there minus the node_modules and such).
Happy Coding!
Fairly Simple!
My favorite method to get type of anything in JS
function getType(entity){
var x = Object.prototype.toString.call(entity)
return x.split(" ")[1].split(']')[0].toLowerCase()
}
my favorite method to check type of anything in JS
function checkType(entity, type){
return getType(entity) === type
}
Use class.name. This also works with function.name.
class TestA {}
console.log(TestA.name); // "TestA"
function TestB() {}
console.log(TestB.name); // "TestB"

Categories

Resources