Comparison with possibly non-existent properties - javascript

My background is more in strongly-typed languages than not, so I will state up front that I have a bias against treating undefined objects in a casual manner. That said, I was considering a simple question of assigning a Boolean value to a variable, where said Boolean comes from the property of some object, and while the object will always exist the property may not.
That sounds more complicated than it really is. Take a look. We start with spinning up some objects early in the code, covering the three possible scenarios:
var object1 = {};
object1.IsDisplayed = true;
var object2 = {};
object2.IsDisplayed = false;
var object3 = {};
// notice we do *not* create the IsDisplayed property here
Sometime later we call the process() function that takes a single argument. Let's call this baseline Solution A.
Solution A
This being JavaScript, that actually works as expected for object1, object2, and object3. But it seems to have a bit of a code smell to me; it is not clear that the author has considered a possibly non-existent property.
function process(renderedItem) {
var rendered = renderedItem.IsDisplayed;
if (rendered) {
// do some stuff here
}
}
I see two possible ways to make this more robust:
Solution B
I like this solution because it is both robust and clear to even the most casual reader that the undefined case has been properly considered. On the downside it is wordy, not nearly as compact as solution C, and it uses a string name of a property, which I have a strong distaste for.
function process(renderedItem) {
var rendered = renderedItem.hasOwnProperty('IsDisplayed')
? renderedItem.IsDisplayed
: false;
if (rendered) {
// do some stuff here
}
}
Solution C
I like this solution because it is robust and compact; it explicitly takes into account a possibly non-existent property; however, it is not nearly as obvious as solution B. I remember early on reading some colleagues' code and thinking,"What the heck?" and suggested deleting the === true as redundant. Also, it still is technically referencing an undefined value (in the case of passing in object3).
function process(renderedItem) {
var rendered = renderedItem.IsDisplayed === true;
if (rendered) {
// do some stuff here
}
}
Is there a clear "best practice" amongst these three variations?

Related

what is the diffrence betwean deepFreeze and true imutibility

Hello and thank you in advanced.
I recently read about object.freez and deep.freez
And considering that js has no immutable structures, I'm now left wondering how this differs from the standert term of immutability...
"quote: As said before JavaScript has no immutable structures, but immutability can be achieved by principles and rules."
how ever seeing: code from :source
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);
}
var obj2 = {
internal: {
a: null
}
};
deepFreeze(obj2);
obj2.internal.a = 'anotherValue'; // fails silently in non-strict mode
obj2.internal.a; // null
I'm now left at a bit perplexed
I thought immutability meant not being able to mutate(change) the values of an object after it has bean created. And as far as i can tell deep.freez achieves exactly that... So what is the difference?
hope this question made sense as i was not able to find any information concerning deep.freez
Kind regards Lost.
Both points are correct - there are no data structures in JavaScript that are immutable by default. For example, there is no data structure you can create which is an "immutable object" at the time of creation.
However, Object.freeze is a method in the JavaScript/ECMAScript specification which freezes the properties of an object after it's been created, essentially making it immutable.
Your deepFreeze function, at its heart, calls Object.freeze. deepFreeze ensures that if an object has a property which is also an object, it will also be frozen.
The article you linked to actually mentions Object.freeze as a method of creating "immutable" objects.

Add Javascript Method

Is there way to create a new global method where I can have variable.myMethod() and it return true or false? Basically, I want to check to see if the variable is undefined and instead of using typeof(variable) == 'undefined' or specifying a function, is it possible to do something like variable.isUndefined() and it would return true or false?
I'm going to go ahead and post this as an answer, so I can go into a bit more detail.
As I mentioned in my comment, you have to be extremely careful about the terms that you use here, as undeclared and undefined mean two very different things.
If a variable is "undeclared", it doesn't exist, thus you cannot attempt to call any methods that might exist on it if it were declared.
If a variable is "undefined", it exists, but it doesn't have a value assigned to it. When this is the case, you can attempt to call methods that may exist on it, however the chances are that they'll fail, since variable doesn't have any value.
Every type in JavaScript is a child of the Object type, therefore you add a method to them, like follows:
Object.prototype.myMethod = function() {
console.log("This is my method");
};
So, in theory, you could create a method to check to see if a value exists, and return a true/false value.
Similarly to what StackOverflow user Barmar pointed out, undefined and null are not children of the Object type, thus your method will not exist.
As other comments have stated, you're probably better of sticking with something like follows:
if (!myVariable) {
// myVariable doesn't have a value
}
I'd like to point out that most of my explanation was unnecessary, as user Barmar pointed out, there is no practical difference between undeclared and undefined.
If a variable is undeclared, it's "value" is essentially read as undefined, thus your method will not exist.
this is a paradox. you can never have a method inside an undefined object because and undefined object does not have anything at all. to make it clear imagine this object
var a = {};
a.b is undefined here, so trying to call a.b.isDefined() can not work because you dont even have b. to make it work you need b defined like this
var a = {b:1,isUndefined:function(){return false}}
so you have to make a generic function that takes objects. This will do the trick
function isUndefined(obj,stringLink){
var arrayLink = stringLink.split(".");
var current = obj[arrayLink[0]];
if(!current)return false;
for(var i =1;i< arrayLink.length;i++){
current = current[arrayLink[i]];
if (!current)return false;
}
return true;
}
it will more or less check for nested object until it reach the target.
if you have
var a = {b:{c:{d:{e:{f:1}}}}}
//this function will do a["b"]["c"]["d"]["e"]["f"]
isUndefined(a,"a.b.c.d.e.f") //gives true
//or you can use
if(a&&a.b&&a.b.c&&a.b.c.d&&a.b.c.d.e&&a.b.c.d.e.f)//lots of work here

Delete objects with delete or by assigning null in Javascript?

How shall I delete objects in Javascript to properly destroy them (call destructors if there are any?) and prevent memory leaks? I've seen two ways:
delete obj;
and
obj = null;
But even after reading this I have not understood what is really the difference and what should I use.
Also, I guess there are not real destructors in Javascript, but in case of complex relationship of multiple objects is it really enough to rely on garbage collector to prevent memory leaks?
One major difference between the two is the value of obj after the operation. Consider
x = 42
x = null
console.log(x) // prints: null
delete x;
console.log(x) // prints: undefined
Assigning null is giving a new value to an existing property. It will still exist after the operation. When you delete a property it is removed entirely.
Google has something to say about delete:
Prefer this.foo = null
Foo.prototype.dispose = function() {
this.property_ = null;
};
Instead of:
Foo.prototype.dispose = function() {
delete this.property_;
};
In modern JavaScript engines, changing the number of properties on an
object is much slower than reassigning the values. The delete keyword
should be avoided except when it is necessary to remove a property
from an object's iterated list of keys, or to change the result of if
(key in obj).
Edit
Some performance test: http://jsperf.com/delete-vs-nullify
The article you have linked says that delete only deletes a reference, therefore there is no difference between the two really performance wise. The only theoretical difference is the garbage collector will be more efficient because it's been explicitly told what to do, but unless you have incredibly large complex objects with many references in hard to reach locations it shouldn't be an issue.
The other answer on the question explains an actual usecase that highlights a difference, ie. removing a property from an object. Accessing it would then give you undefined rather than null.
Here's an example of the latter: http://jsfiddle.net/YPSqM/
function cat() { this.name = 'mittens'; };
var myCat = new cat();
alert(myCat.name); // 'mittens'
myCat.name = null;
alert(myCat.name === null); // 'true' as it is null but not undefined
delete myCat.name;
alert(myCat.name === undefined); // 'true' as it is undefined (but not null)

Javascript collection

sorry for noobie question. Can you explain please, what is the difference between:
1. var a = [];
a['b'] = 1;
2. var a = {};
a['b'] = 1;
I could not find article in the internet so wrote here.
Literals
The [] and {} are called the array and object literals respectively.
var x = [] is short for var x = new Array();
and var y = {} is short for var y = new Object();
Arrays
Arrays are structures with a length property. You can access values via their numeric index.
var x = [] or var x = new Array();
x[0] = 'b';
x[1] = 'c';
And if you want to list all the properties you do:
for(var i = 0; i < x.length; i++)
console.log(x[i]);// numeric index based access.
Performance tricks and gotchas
1. Inner-caching the length property
The standard array iteration:
for (var i = 0; i < arr.length; i++) {
// do stuff
};
Little known fact: In the above scenario, the arr.length property is read at every step of the for loop. Just like any function you call there:
for (var i = 0; i < getStopIndex(); i++) {
// do stuff
};
This decreases performance for no reason. Inner caching to the rescue:
for (var i = 0, len = arr.length; i < len; i++) {
// enter code here
};
Here's proof of the above.
2. Don't specify the Array length in the constructor.
// doing this:
var a = new Array(100);
// is very pointless in JS. It will result in an array with 100 undefined values.
// not even this:
var a = new Array();
// is the best way.
var a = [];
// using the array literal is the fastest and easiest way to do things.
Test cases for array definition are available here.
3. Avoid using Array.prototype.push(arr.push)
If you are dealing with large collections, direct assignment is faster than using the Array.prototype.push(); method.
myArray[i] = 0; is faster than myArray.push(0);, according to jsPerf.com test cases.
4. It is wrong to use arrays for associative assignments.
The only reason why it works is because Array extends the Object class inside the core of the JS language. You can just as well use a Date(); or RegEx(); object for instance. It won't make a difference.
x['property'] = someValue MUST always be used with Objects.
Arrays should only have numeric indexes. SEE THIS, the Google JS development guidelines! Avoid for (x in arr) loops or arr['key'] = 5;.
This can be easily backed up, look HERE for an example.
var x = [];
console.log(x.prototype.toString.call);
 will output: [object Array]
This reveals the core language's 'class' inheritance pattern.  
var x = new String();
console.log(x.prototype.toString.call);
will output [object String].
5. Getting the minimum and maximum from an array.
A little known, but really powerful trick:
function arrayMax(arr) {
return Math.max.apply(Math, arr);
};
, respectively:
function arrayMin(arr) {
return Math.min.apply(Math, arr);
};
Objects
With an object you can only do:
var y = {} or var y = new Object();
y['first'] = 'firstValue' is the same as y.first = 'firstValue', which you can't do with an array. Objects are designed for associative access with String keys.
And the iteration is something like this:
for (var property in y) {
if (y.hasOwnProperty(property)) {
console.log(y.property);
};
};
Performance tricks and gotchas
1. Checking if an object has a property.
Most people use Object.prototype.hasOwnProperty. Unfortunately that often gives erroneous results leading to unexpected bugs.
Here's a good way to do it:
function containsKey(obj, key) {
return typeof obj[key] !== 'undefined';
};
2. Replacing switch statements.
One of the simple but efficient JS tricks is switch replacement.
switch (someVar) {
case 'a':
doSomething();
break;
case 'b':
doSomethingElse();
break;
default:
doMagic();
break;
};
In most JS engines the above is painfully slow. When you are looking at three possible outcomes, it doesn't make a difference, but what if you had tens or hundreds?
The above can easily be replaced with an object. Don't add the trailing (), this is not executing the functions, but simply storing references to them:
var cases = {
'a': doSomething,
'b': doSomethingElse,
'c': doMagic
};
Instead of the switch:
var x = ???;
if (containsKey(cases, x)) {
cases[x]();
} else {
console.log("I don't know what to do!");
};
3. Deep-cloning made easy.
function cloneObject(obj) {
var tmp = {};
for (var key in obj) {
tmp[key] = fastDeepClone(obj[key];
};
return tmp;
}
function cloneArr(arr) {
var tmp = [];
for (var i = 0, len = arr.length; i < len; i++) {
tmp[i] = fastDeepClone(arr[i]);
}
return tmp;
}
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
};
function isArray(obj) {
return obj instanceof Array;
}
function isObject(obj) {
var type = typeof obj;
return type === 'object' && obj !== null || type === 'function';
}
function fastDeepClone(obj) {
if (isArray(obj)) {
return cloneArr(obj);
} else if (isObject(obj)) {
return cloneObject(obj);
} else {
return obj;
};
};
HERE is the deep clone function in action.
Auto-boxing
As a dynamically typed language, JavaScript is limited in terms of native object types:
Object
Array
Number
Boolean
Date
RegEx
Error
Null is not a type, typeof null is object.
What's the catch? There is a strong distinction between primitive and non-primitive objects.
var s = "str";
var s2 = new String("str");
They do the same thing, you can call all string methods on s and s2.
Yet:
type of s == "string"; // raw data type
type of s2 == "object" // auto-boxed to non-primitive wrapper type
s2.prototype.toString.call == "[object String]";
You may hear in JS everything is an object. That's not exactly true, although it's a really easy mistake to make.
In reality there are 2 types, primitives and objects, and when you call s.indexOf("c"), the JS engine will automatically convert s to its non-primitive wrapper type, in this case object String, where all the methods are defined on the String.prototype.
This is called auto-boxing. The Object.prototype.valueOf(obj) method is a way to force the cast from primitive to non-primitive. It's the same behaviour a language like Java introduces for many of it's own primitives, specifically the pairs: int - Integer, double - Double, float - Float, etc.
Why should you care?
Simple:
function isString(obj) {
return typeof obj === "string";
}
isString(s); // true
isString(s2); // false
So if s2 was created with var s2 = new String("test") you are getting a false negative, even for an otherwise conceivably simple type check. More complex objects also bring with themselves a heavy performance penalty.
A micro-optimization as some would say, but the results are truly remarkable, even for extremely simple things such as string initialisation. Let's compare the following two in terms of performance:
var s1 = "this_is_a_test"
and
var s2 = new String("this_is_a_test")
You will probably expected matching performance across the board, but rather surprisingly the latter statement using new String is 92% slower than the first one, as proven here.
Functions
1. Default parameters
The || operator is the simplest possible way of defaulting. Why does it work? Because of truthy and falsy values.
When evaluated in a logical condition, undefined and null values will autocast to false.
A simple example(code HERE):
function test(x) {
var param = x || 5;
// do stuff with x
};
2. OO JS
The most important thing to understand is that the JavaScript this object is not immutable. It is simply a reference that can be changed with great ease.
In OO JS, we rely on the new keyword to guarantee implicit scope in all members of a JS Class. Even so, you can easily change the scope, via Function.prototype.call and Function.prototype.apply.
Another very important thing is the Object.prototype. Non-primitive values nested on an objects prototype are shared, while primitive ones are not.
Code with examples HERE.
A simple class definition:
function Size(width, height) {
this.width = width;
this.height = height;
};
A simple size class, with two members, this.width and this.height.
In a class definition, whatever has this in front of it, will create a new reference for every instance of Size.
Adding methods to classes and why the "closure" pattern and other "fancy name pattern" are pure fiction
This is perhaps where the most malicious JavaScript anti-patterns are found.
We can add a method to our Size class in two ways.
Size.prototype.area = function() {
return this.width * this.height;
};
Or:
function Size2(width, height) {
this.width = width;
this.height = height;
this.area = function() {
return this.width * this.height;
}
}
var s = new Size(5, 10);
var s2 = new Size2(5, 10);
var s3 = new Size2(5, 10);
var s4 = new Size(5, 10);
// Looks identical, but lets use the reference equality operator to test things:
s2.area === s3.area // false
s.area === s4.area // true
The area method of Size2 is created for every instance.
This is completely useless and slow, A LOT slower. 89% to be exact. Look HERE.
The above statement is valid for about 99% of all known "fancy name pattern". Remember the single most important thing in JS, all those are nothing more than fiction.
There are strong architectural arguments that can be made, mostly revolved around data encapsulation and the usage of closures.
Such things are unfortunately absolutely worthless in JavaScript, the performance loss simply isn't worth it. We are talking about 90% and above, it's anything but negligible.
3. Limitations
Because prototype definitions are shared among all instances of a class, you won't be able to put a non-primitive settings object there.
Size.prototype.settings = {};
Why? size.settings will be the same for every single instance.
So what's with the primitives?
Size.prototype.x = 5; // won't be shared, because it's a primitive.
// see auto-boxing above for primitive vs non-primitive
// if you come from the Java world, it's the same as int and Integer.
The point:
The average JS guy will write JS in the following way:
var x = {
doStuff: function(x) {
},
doMoreStuff: 5,
someConstant: 10
}
Which is fine (fine = poor quality, hard to maintain code), as long as you understand that is a Singleton object, and those functions should only be used in global scope without referencing this inside them.
But then it gets to absolutely terrible code:
var x = {
width: 10,
height: 5
}
var y = {
width: 15,
height: 10
}
You could have gotten away with: var x = new Size(10, 5); var y = new Size(15, 5);.
Takes longer to type, you need to type the same thing every time. And again, it's A LOT SLOWER. Look HERE.
Poor standards throughout
This can be seen almost anywhere:
function() {
// some computation
var x = 10 / 2;
var y = 5;
return {
width: x,
height: y
}
}
Again with the alternative:
function() {
var x = 10 / 2;
var y = 5;
return new Size(10, 5);
};
The point: USE CLASSES WHEREVER APPROPRIATE!!
Why? Example 1 is 93% Slower. Look HERE.
The examples here are trivial, but they illustrate something being ignored in JS, OO.
It's a solid rule of thumb not to employ people who think JS doesn't have classes and to get jobs from recruiters talking about "Object Orientated" JS.
Closures
A lot of people prefer them to the above because it gives them a sense of data encapsulation. Besides the drastic 90% performance drop, here's something equally easy to overlook. Memory leaks.
function Thing(someParam) {
this.someFn = function() {
return someParam;
}
}
You've just created a closure for someParam. Why is this bad? First, it forces you to define class methods as instance properties, resulting in the big performance drop.
Second, it eats up memory, because a closure will never get dereferenced. Look here for proof. Sure, you do get some fake data encapsulation, but you use three times the memory with a 90% performance drop.
Or you can add #private and get a way with an underscore function name.
Other very common ways of generating closures:
function bindSomething(param) {
someDomElement.addEventListener("click", function() {
if (param) //do something
else // do something else
}, false);
}
param is now a closure! How do you get rid of it? There are various tricks, some found here. The best possible approach, albeit more rigorous is to avoid using anonymous functions all-together, but this would require a way to specify scopes for event callbacks.
Such a mechanism is only available in Google Closure, as far as I know.
The singleton pattern
Ok, so what do I do for singletons? I don't want to store random references. Here's a wonderful idea shamelessly stolen from Google Closure's base.js
/**
* Adds a {#code getInstance} static method that always return the same instance
* object.
* #param {!Function} ctor The constructor for the class to add the static
* method to.
*/
function addSingletonGetter(ctor) {
ctor.getInstance = function() {
if (ctor.instance_) {
return ctor.instance_;
}
return ctor.instance_ = new ctor;
};
};
It's Java-esque, but it's a simple and powerful trick. You can now do:
project.some.namespace.StateManager = function() {
this.x_ = 5;
};
project.some.namespace.prototype.getX = function() { return x; }
addSingletonGetter(project.some.namespace.StateManager);
How is this useful? Simple. In all other files, every time you need to reference project.some.namespace.StateManager, you can write:
project.some.namespace.StateManager.getInstance(). This is more awesome than it looks.
You can have global state with the benefits of a class definition (inheritance, stateful members, etc.) and without polluting the global namespace.
The single instance pattern
You may now be tempted to do this:
function Thing() {
this.someMethod = function() {..}
}
// and then use it like this:
Thing.someMethod();
That is another big no-no in JavaScript. Remember, the this object is only guaranteed to be immutable when the new keyword is used. The magic behind the above code is interesting. this is actually the global scope, so without meaning to you are adding methods to the global object. And you guessed it, those things never get garbage collected.
There is nothing telling JavaScript to use something else. A function on it's own doesn't have a scope. Be really careful what you do with static properties. To reproduce a quote I once read, the JavaScript global object is like a public toilet. Sometimes you have no choice but to go there, yet try and minimise contact with the surfaces as much as possible.
Either stick to the above Singleton pattern or use a settings object nested under a namespace.
Garbage collection in JavaScript
JavaScript is a garbage collected language, but JavaScript GC is often rather poorly understood. The point is again speed. This is perhaps all too familiar.
// This is the top of a JavaScript file.
var a = 5;
var b = 20;
var x = {};//blabla
// more code
function someFn() {..}
That is bad, poor performance code. The reason is simple. JS will garbage collect a variable and free up the heap memory it holds only when that variable gets de-scoped, e.g. there are no references to it anywhere in the memory.
For example:
function test(someArgs) {
var someMoreStuf = // a very big complex object;
}
test();
Three things:
Function arguments are transformed into local definitions
Inner declarations are hoisted.
All the heap memory allocated for inner variables is freed up when the function finishes execution.
Why?
Because they no longer belong to the "current" scope. They are created, used, and destroyed. There are no closures either, so all the memory you've used is freed up through garbage collection.
For that reason, you should never, your JS files should never look like this, as global scope will just keep polluting memory.
var x = 5;
var y = {..}; //etc;
Alright, now what?
Namespaces.
JS doesn't have namespaces per say, so this isn't exactly a Java equivalent, yet from a codebase administration perspective you get what you want.
var myProject = {};
myProject.settings = {};
myProject.controllers = {};
myProject.controlls.MainController = function() {
// some class definition here
}
Beautiful. One global variable. Proper project structure.
With a build phase, you can split your project across files, and get a proper dev environment.
There's no limit to what you can achieve from here.
Count your libraries
Having had the pleasure of working on countless codebases, the last and most important argument is to be very mindful of your code dependencies. I've seen programmers casually adding jQuery into the mix of the stack for a simple animation effect and so forth.
Dependency and package management is something the JavaScript world hadn't addresses for the longest time, until the creation of tools like Bower. Browsers are still somewhat slow, and even when they're fast, internet connections are slow.
In the world of Google for instance, they go through the lengths of writing entire compilers just to save bytes, and that approach is in many ways the right mentality to have in web programming. And I uphold Google in very high regard as their JS library powers apps like Google Maps, which are not only insanely complex, but also work everywhere.
Arguably JavaScript has an immense variety of tools available, given its popularity, accessibility, and to some extent very low quality bar the ecosystem as a whole is willing to accept.
For Hacker News subscribers, a day doesn't go by without a new JS library out there, and they are certainly useful but one cannot ignore the fact that many of them re-implement the exact same concerns without any strong notion of novelty or any killer ideas and improvements.
It's a strong rule of thumb to resist the urge of mixing in all the new toys before they have the time to prove their novelty and usefulness to the entire ecosystem and to strongly distinguish between Sunday coding fun and production deployments.
If your <head></head> tag is longer than this post, you're doing it all wrong.
Testing your knowledge of JavaScript
A few "perfectionist" level tests:
http://perfectionkills.com/javascript-quiz/, thanks to Kangax.
http://javascript-puzzlers.herokuapp.com/
A collection of objects? Use this notation (JavaScript arrays):
var collection = [ {name:"object 1"} , {name:"object 2"} , {name:"object 3"} ];
To put a new element into your collection:
collection.push( {name:"object 4"} );
In JavaScript all objects are associative arrays. In first case you create an array in the second case you created an empty object which is array too :).
So in JS you can work with any object as with array:
var a = {};
a["temp"] = "test";
And as object:
var a = {};
a.temp = "test";
I would use an array of objects:
collection = [
{ "key":"first key", "value":"first value" },
{ "key":"second key", "value":"second value" }
];
etc
1) Is an Array
2) Is an Object
With Array all is usual as in other languages
With Object also.
- You can get value a.b == 1
- But in JS you can also get value with such syntax a["b"] == 1
This could be usefull when key look like something this "some key", in this case you can't use "chaining"
also this usefull if key is the variable
you can write like this
function some(f){
var Object = {name: "Boo", age: "foo"}, key;
if(f == true){
key = "name";
}else{
key = "age";
}
return Object[key];
}
but I want to use it as collection, which I have to choose?
This depends of what data you want to store

Cloning: what's the fastest alternative to JSON.parse(JSON.stringify(x))?

What's the fastest alternative to
JSON.parse(JSON.stringify(x))
There must be a nicer/built-in way to perform a deep clone on objects/arrays, but I haven't found it yet.
Any ideas?
No, there is no build in way to deep clone objects.
And deep cloning is a difficult and edgey thing to deal with.
Lets assume that a method deepClone(a) should return a "deep clone" of b.
Now a "deep clone" is an object with the same [[Prototype]] and having all the own properties cloned over.
For each clone property that is cloned over, if that has own properties that can be cloned over then do so, recursively.
Of course were keeping the meta data attached to properties like [[Writable]] and [[Enumerable]] in-tact. And we will just return the thing if it's not an object.
var deepClone = function (obj) {
try {
var names = Object.getOwnPropertyNames(obj);
} catch (e) {
if (e.message.indexOf("not an object") > -1) {
// is not object
return obj;
}
}
var proto = Object.getPrototypeOf(obj);
var clone = Object.create(proto);
names.forEach(function (name) {
var pd = Object.getOwnPropertyDescriptor(obj, name);
if (pd.value) {
pd.value = deepClone(pd.value);
}
Object.defineProperty(clone, name, pd);
});
return clone;
};
This will fail for a lot of edge cases.
Live Example
As you can see you can't deep clone objects generally without breaking their special properties (like .length in array). To fix that you have to treat Array seperately, and then treat every special object seperately.
What do you expect to happen when you do deepClone(document.getElementById("foobar")) ?
As an aside, shallow clones are easy.
Object.getOwnPropertyDescriptors = function (obj) {
var ret = {};
Object.getOwnPropertyNames(obj).forEach(function (name) {
ret[name] = Object.getOwnPropertyDescriptor(obj, name);
});
return ret;
};
var shallowClone = function (obj) {
return Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
};
I was actually comparing it against angular.copy
You can run the JSperf test here:
https://jsperf.com/angular-copy-vs-json-parse-string
I'm comparing:
myCopy = angular.copy(MyObject);
vs
myCopy = JSON.parse(JSON.stringify(MyObject));
This is the fatest of all test I could run on all my computers
The 2022 solution for this is to use structuredClone
See : https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
structuredClone(x)
Cyclic references are not really an issue. I mean they are but that's just a matter of proper record keeping. Anyway quick answer for this one. Check this:
https://github.com/greatfoundry/json-fu
In my mad scientist lab of crazy javascript hackery I've been putting the basic implementation to use in serializing the entirety of the javascript context including the entire DOM from Chromium, sending it over a websocket to Node and reserializing it successfully. The only cyclic issue that is problematic is the retardo navigator.mimeTypes and navigator.plugins circle jerking one another to infinity, but easily solved.
(function(mimeTypes, plugins){
delete navigator.mimeTypes;
delete navigator.plugins;
var theENTIREwindowANDdom = jsonfu.serialize(window);
WebsocketForStealingEverything.send(theENTIREwindowANDdom);
navigator.mimeTypes = mimeTypes;
navigator.plugins = plugins;
})(navigator.mimeTypes, navigator.plugins);
JSONFu uses the tactic of creating Sigils which represent more complex data types. Like a MoreSigil which say that the item is abbreviated and there's X levels deeper which can be requested. It's important to understand that if you're serializing EVERYTHING then it's obviously more complicated to revive it back to its original state. I've been experimenting with various things to see what's possible, what's reasonable, and ultimately what's ideal. For me the goal is a bit more auspicious than most needs in that I'm trying to get as close to merging two disparate and simultaneous javascript contexts into a reasonable approximation of a single context. Or to determine what the best compromise is in terms of exposing the desired capabilities while not causing performance issues. When you start looking to have revivers for functions then you cross the land from data serialization into remote procedure calling.
A neat hacky function I cooked up along the way classifies all the properties on an object you pass to it into specific categories. The purpose for creating it was to be able to pass a window object in Chrome and have it spit out the properties organized by what's required to serialize and then revive them in a remote context. Also to accomplish this without any sort of preset cheatsheet lists, like a completely dumb checker that makes the determinations by prodding the passed value with a stick. This was only designed and ever checked in Chrome and is very much not production code, but it's a cool specimen.
// categorizeEverything takes any object and will sort its properties into high level categories
// based on it's profile in terms of what it can in JavaScript land. It accomplishes this task with a bafflingly
// small amount of actual code by being extraordinarily uncareful, forcing errors, and generally just
// throwing caution to the wind. But it does a really good job (in the one browser I made it for, Chrome,
// and mostly works in webkit, and could work in Firefox with a modicum of effort)
//
// This will work on any object but its primarily useful for sorting the shitstorm that
// is the webkit global context into something sane.
function categorizeEverything(container){
var types = {
// DOMPrototypes are functions that get angry when you dare call them because IDL is dumb.
// There's a few DOM protos that actually have useful constructors and there currently is no check.
// They all end up under Class which isn't a bad place for them depending on your goals.
// [Audio, Image, Option] are the only actual HTML DOM prototypes that sneak by.
DOMPrototypes: {},
// Plain object isn't callable, Object is its [[proto]]
PlainObjects: {},
// Classes have a constructor
Classes: {},
// Methods don't have a "prototype" property and their [[proto]] is named "Empty"
Methods: {},
// Natives also have "Empty" as their [[proto]]. This list has the big boys:
// the various Error constructors, Object, Array, Function, Date, Number, String, etc.
Natives: {},
// Primitives are instances of String, Number, and Boolean plus bonus friends null, undefined, NaN, Infinity
Primitives: {}
};
var str = ({}).toString;
function __class__(obj){ return str.call(obj).slice(8,-1); }
Object.getOwnPropertyNames(container).forEach(function(prop){
var XX = container[prop],
xClass = __class__(XX);
// dumping the various references to window up front and also undefineds for laziness
if(xClass == "Undefined" || xClass == "global") return;
// Easy way to rustle out primitives right off the bat,
// forcing errors for fun and profit.
try {
Object.keys(XX);
} catch(e) {
if(e.type == "obj_ctor_property_non_object")
return types.Primitives[prop] = XX;
}
// I'm making a LOT flagrant assumptions here but process of elimination is key.
var isCtor = "prototype" in XX;
var proto = Object.getPrototypeOf(XX);
// All Natives also fit the Class category, but they have a special place in our heart.
if(isCtor && proto.name == "Empty" ||
XX.name == "ArrayBuffer" ||
XX.name == "DataView" ||
"BYTES_PER_ELEMENT" in XX) {
return types.Natives[prop] = XX;
}
if(xClass == "Function"){
try {
// Calling every single function in the global context without a care in the world?
// There's no way this can end badly.
// TODO: do this nonsense in an iframe or something
XX();
} catch(e){
// Magical functions which you can never call. That's useful.
if(e.message == "Illegal constructor"){
return types.DOMPrototypes[prop] = XX;
}
}
// By process of elimination only regular functions can still be hanging out
if(!isCtor) {
return types.Methods[prop] = XX;
}
}
// Only left with full fledged objects now. Invokability (constructor) splits this group in half
return (isCtor ? types.Classes : types.PlainObjects)[prop] = XX;
// JSON, Math, document, and other stuff gets classified as plain objects
// but they all seem correct going by what their actual profiles and functionality
});
return types;
};

Categories

Resources