I am trying to understand the Object.freeze method of ECMAscript.
My understanding was that it essentially stops changes to all the properties of an object. MDN documentation says:
Prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed.
This does not seem to be the case, but perhaps I have misinterpreted the docs.
Here is my object, with its enumerable property exampleArray
function myObject()
{
this.exampleArray = [];
}
var obj = new myObject();
obj.exampleArray[0] = "foo";
Now if I freeze the object, I would expect the exampleArray property to be frozen too, as in it can no longer be changed in any way.
Object.freeze(obj);
obj.exampleArray[1] = "bar";
console.log(obj.exampleArray.length); // logs 2
"bar" has been added to the array, thus the frozen object has been changed. My immediate solution is to just freeze the desired property:
Object.freeze(obj.exampleArray);
obj.exampleArray[2] = "boo";
Now changing the array throws an error, as desired.
However, I am developing my application and I don't yet know what will be assigned to my object. My use case is that I have some game objects which are initialized (from an XML file) when the game starts. After this, I do not want to be able to change any of their properties accidentally.
Perhaps I am misusing the freeze method? I would like to be able to freeze the whole object, a sort of recursive freeze. The best solution I can think of here is to loop through the properties and freeze each one.
I've already searched for this question and the only answer says it's an implementation bug. I am using the newest version of Chrome. Any help is appreciated.
Object.freeze is a shallow freeze.
If you look at the description in the docs, it says:
Values cannot be changed for data properties. Accessor properties (getters and setters) work the same (and still give the illusion that you are changing the value). Note that values that are objects can still be modified, unless they are also frozen.
If you want to deep-freeze an object, here's a good recursive example
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;
}
function myObject() {
this.exampleArray = [];
}
var obj = deepFreeze(new myObject());
obj.exampleArray[0] = "foo";
console.log(obj); // exampleArray is unchanged
Set the property descriptors for the object to writable:false, configurable:false using Object.defineProprties; then call Object.preventExtensions on the object. See How to create static array in javascript.
Related
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.
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)
A project I've been working on recently gave me the need to create a function which can return a complete copy of a JSON object, recursively copying any internal objects. After a couple of failed attempts, I came up with this:
function copyObj(obj) {
var copy;
if (obj instanceof Array) {
copy = [];
for (var i in obj) {
copy.push(copyObj(obj[i]));
}
}
else if (obj instanceof Object) {
copy = {};
for (var prop in obj) {
copy[prop] = copyObj(obj[prop]);
}
}
else {
copy = obj;
}
return copy;
}
The function works perfectly for my purposes, which are to copy objects that will only ever contain primitive types, arrays, and nested generic JSON objects. For example, it will return a flawless copy of this: { prop1:0, prop2:'test', prop3:[1, 2, 3], prop4:{ subprop1:['a', 'b', 'c'], subprop2:false } }.
There's one thing about this function that's nagging at me, though - its inability to handle any other types of objects (e.g. the RegExp object). I'd like to improve on it by adding the capability to handle them, but at the same time I'd really rather not just have a huge wall of else if (obj instanceof [insert object type here]). As such, my question is this: Is there a simple way in JavaScript of differentiating between a generic object (i.e. one declared as var obj = { }) and one with a proper prototype/constructor? And if so, is there also a simple generalized way of copying such objects? My expectation for the second part of the question is no, and that I'd still need special handling to call constructors, but I'd still like to know with certainty either way.
P.S. In case anyone was curious about the context, the project requires me to manipulate a large list of items on a server, but in different ways for different connected clients. The easiest way I could think of to handle that was to create one master list and then have the server clone a fresh copy to manipulate without altering the master list for every new client that connects, hence the need for copyObj().
Edit: I probably should have mentioned this in the original question - this is running with node.js as a server, not in a browser, so browser cross-compatibility isn't an issue.
Edit 2: In the interest of not cluttering the comments too much, I'll mention it here: I tried a quick benchmarking of my copyObj() function against the JSON.parse(JSON.stringify(obj)) exploit using the example object above. My version seems to run in about 75% of the time that the JSON method takes (1 million copies took ~3.2 seconds for mine and ~4.4 seconds for JSON). So that makes me feel better about having taken the time to write my own.
Edit 3: Working off of the list of object types in Vitum.us's answer, I threw together an updated version of my copyObj() function. I haven't tested it extensively, and the performance is about 2x worse than the old version, but I think it should actually work for all built-in types (assuming that list was complete).
function copyObjNew(obj) {
var copy;
if (obj.constructor === Object) {
// Generic objects
copy = {};
for (var prop in obj) {
copy[prop] = copyObjNew(obj[prop]);
}
}
else if (obj.constructor === Array) {
// Arrays
copy = [];
for (var i in obj) {
copy.push(copyObjNew(obj[i]));
}
}
else if (obj.constructor === Number || obj.constructor === String || obj.constructor === Boolean) {
// Primitives
copy = obj;
}
else {
// Any other type of object
copy = new obj.constructor(obj);
}
return copy;
}
I'm using the .constructor property now, as Mike suggested, and it seems to be doing the trick. I've tested it so far with RegExp and Date objects, and they both seem to copy correctly. Do any of you see anything blatantly (or subtly) incorrect about this?
One way of detecting plain JS objects (created with {} or new Object) is to use the jQuery method jQuery.isPlainObject.
However, the documentation says that "Host objects have a number of inconsistencies which are difficult to robustly feature detect cross-platform. As a result of this, $.isPlainObject() may evaluate inconsistently across browsers in certain instances." Whether this works reliably with node.js should be tested.
Edit: in response to your comment: you can use jQuery with node.js, see this question: Can I use jQuery with Node.js?
Otherwise it is also possible to just copy the jQuery implementation of the method to your project, as the MIT license (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) seems to permit this. jQuery implementation:
isPlainObject: function( obj ) {
// Not plain objects:
// - Any object or value whose internal [[Class]] property is not "[object Object]"
// - DOM nodes
// - window
if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
// Support: Firefox <20
// The try/catch suppresses exceptions thrown when attempting to access
// the "constructor" property of certain host objects, ie. |window.location|
// https://bugzilla.mozilla.org/show_bug.cgi?id=814622
try {
if ( obj.constructor &&
!core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
return false;
}
} catch ( e ) {
return false;
}
// If the function hasn't returned already, we're confident that
// |obj| is a plain object, created by {} or constructed with new Object
return true;
}
You can use this to detect if a object is a regular expression
Object.prototype.toString.call( regexpObject ) == "[object RegExp]"
This is the way mentioned in the specification for getting the class of object.
From ECMAScript 5, Section 8.6.2 Object Internal Properties and Methods:
The value of the [[Class]] internal property is defined by this specification for every kind of built-in object. The value of the [[Class]] internal property of a host object may be any String value except one of "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String". The value of a [[Class]] internal property is used internally to distinguish different kinds of objects. Note that this specification does not provide any means for a program to access that value except through Object.prototype.toString (see 15.2.4.2).
A RegExp is a class of object defined in the spec at Section 15.10 RegExp(RegularExpression)Objects:
A RegExp object contains a regular expression and the associated flags.
Then, you can copy a RegExp object using new RegExp()
var oldObject = /[a-z]+/;
var newObject = new RegExp(oldObject);
Ok, I'm trying to track any changes made to a huge form on a web application. When the page is loaded, I create a JS object that 'captures' the initial state of all input fields (selects, radio buttons, checkboxes etc...).When the user alters the value of any of the literally hundreds of input elements, the new value is tracked in a second object. When the user clicks Update, these two objects are compared and only those values that have been changed are sent, to update the data accordingly.
Rather then building 2 completely separate objects, I thought it wise to use inheritance:
var initialState = getInitialState();//eg:{bar:'1',foo:'bar',atom:'bomb',some:{nested:'objects'}}
var tracker = Object.create(initialState);
As things go, the tracker object might end up looking something like this:
{bar:'0',foo:'bar',atom:'peace',some:{nested:'objects'}}
When calling JSON.stringify on this object in FF and chrome, all is well: only the objects' own properties are returned. Not so in IE: the tracker has no prototype property, so it would appear that Object.create creates copies rather then inheritance chains?tracker.__proto__ === initialState returns true, whereas tracker.prototype === initialState evaluates to false, in fact the tracker.prototype property is undefined.
Question1: is there an alternative way to set up an inheritance chain in IE that allows me to peel away the unchanged prototype values?
Question2:I'd also like a method -if at all possible- to set up an inheritance chain that allows for nested objects. As things are now, the nested objects are dealt with by iterating over the main object, using a recursive function. Kind of silly, since that's what I'm trying to omit.
In short:I want to know if this is out there:
var a = {bar:'1',foo:'bar',atom:'bomb',some:{nested:'objects'}};
var b = Object.magicDeepCreate(a);//<=== preferably X-browser
b.bar = '0';
b.bar.some.nested = 'stuff';
console.log(JSON.stringify(b));
//{"bar":"0","some":{"nested":"stuff"}}
As always: no jQuery tag, means no jQuery Note: by IE I mean that monstrosity IE8, not IE9 (company policy, sadly)
tracker.__proto__ === initialState returns true, whereas tracker.prototype === initialState evaluates to false, in fact the tracker.prototype property is undefined.
The __proto__ property is non-standard and FF-only. To get the prototype object of an object, use Object.getPrototypeOf(). The prototype property of function objects is a property referencing the object from which all instances of that function (created using new) inherit.
Not so in IE
Object.create() is not supported at all in IE8. Did you use the common shim or does it silently fail? Or did you even use a function that really copies all properties?
Object.magicDeepCreate(a), preferably X-browser
That should be simple, assuming that all target browsers implement Object.create:
Object.deepCreate = function deepCreate(o) {
var res = Object.create(o);
for (var i in o)
if (Object.hasOwnProperty(o, i) && typeof o[i] == "object")
res[i] = deepCreate(o[i]);
return res;
};
stringify only those that have been altered
That should be standard behaviour of JSON.stringify - the prototype object is not taken into account.
However, I'm not sure why you need inheritance at all for that tracker object. Just use an empty object, and add all properties that have been altered. If you want to delete those that have been reset to initial state, you could store that in an extra object to compare with - but there is no reason for inheritance. Just use:
Object.emptyStructure = function s(o){
var res = {};
for (var i in o)
if (typeof o[i] == "object")
res[i] = s(o[i]);
return res;
};
var initialState = getInitialState();
var tracker = Object.emptyStructure(initialState);
// set:
if (newVal == initialState.some.nested)
delete tracker.some.nested;
else
tracker.some.nested = newVal;
var obj = {
destroy: function(){this = null;}
};
obj.destroy();
This works in Chrome, however firefox is throwing an error referencing this for some reason. Is there a better way to kill this object within a method?
Error:
invalid assignment left-hand side
[Break On This Error] destroy: function(){this = null;}
Not sure why Chrome allows for it but you can't assign a value to this. You can reference this, but you can't assign a value to it.
If you have some array destruction you want to perform you can reference this.myArrayName within your destroy method and free up whatever you're trying to release, but you can't just assign null to this to destroy an instance.
I suppose you could try something like this:
var foo = {
// will nullify all properties/methods of foo on dispose
dispose: function () { for (var key in this) this[key] = null; }
}
foo.dispose();
Pretty much as close as you can get to legally nullifying "this"...
Happy coding.
B
Call me old fashion, but:
foo = null;
I'm not sure why you're making this difficult. Javascript is a garbage collected language. All you have to do to allow something to be freed is to make sure there are no more references to it anywhere.
So, if you start with:
var obj = {
data: "foo";
};
and now you want to get rid or "free" that object, all you have to do is clear the reference to it with:
obj = null;
Since there are no longer any references in your code to that data structure that you originally defined and assigned to obj, the garbage collector will free it.
An object cannot destroy itself (because other things may have references to it). You allow it to be freed by removing all references to it. An object can clear out it's own references to other things, though that is generally not required as removing all references to the object itself will also take care of the references it holds (with the exception of some bugs with circular references between JS and the DOM in certain older browsers - particular IE).
One time when you might explicitly "delete" something is if you have a property on an object that you wish to remove. So, if you have:
var obj = {
data: "foo",
count: 4
};
And you wish to remove the "data" property, you can do that with this:
delete obj.data;
of if the property/key was assigned programmatically via a variable like this:
var key = "xxx";
obj[key] = "foo";
you can remove that key with:
delete obj[key];