I just found out that javascript has a delete statement. I've read a bit about it and am not much the wiser.
So I am hoping to get a functional definition of when I should use it, if at all. So I know I can delete properties of an object; as is made obvious by this fiddle:
var myData = {a:"hello",b:"world"};
alert(myData.b);
delete myData.b;
alert(myData.b);
Which shows "world" then undefined in successive alerts. However, you cannot use delete like this (as one might in C++):
function data() {
this.attribute1 = "aww";
this.attribute2 = "poo";
}
var myData = new data();
delete myData;
Here delete returns false indicating that you cannot delete myData. I used to work primarily in C++ and this was like the whole idea of delete. I can't think of any reason I would use delete to remove properties. Should I ever worry about using delete to mark memory to be freed? Like if I do something like this.
var myData = new data();
... //do stuff
myData = new data();
Addition
So I dug up the post that confused me. The most upvoted answer on this question states (as quoted from the Apple Javascript Coding Guidelines):
Use delete statements. Whenever you create an object using a new statement, pair it with a delete statement. This ensures that all of the memory associated with the object, including its property name, is available for garbage collection. The delete statement is discussed more in “Freeing Objects.”
So, if I understand some of the comments and answers I've been given, this statement is not accurate, because you cannot even call delete on an object created using a new statement.
According to mozilla's developer documents, delete does not work that way.
The delete operator deletes a property from an object, it does not delete the object itself.
So instead of using it as you have demonstrated, you would use it more like the following:
myGlobalObject = {};
var myObject = {};
myObject.propertyA = "blah";
// Do some stuff
delete myObject.propertyA; // This works because you're deleting a property off myObject
delete myGlobalObject; // This works because myGlobalObject is a property of the global object.
delete myObject; // This does NOT work - most likely because you declared it using the var keyword
This doesn't actually do garbage collection though. Also if myObject has a prototype up the chain that has propertyA it would still inherit that property through the prototype.
For more indepth information feel free to check out the developer documents:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete
delete on its own specifically states:
The delete operator removes a property from an object.
You might remove a property if you don't want it included in data sent to a server, or used by other code, e.g., something that automatically takes object data and turns it into a table.
In general you'd never use it for memory management, a possible exception being if you had a huge chunk of data in an object (like received from the back end) that you explicitly don't need.
Related
I have what seems like it should be a simple operation. For each bridgedSection, I check for a potentialSection with an id that matches the bridged.referenceSection
Then I take that result, parse the HTML on the object with Cherio, make a slight modification (using an id for testing), and then store both the bridgedSection and the modified result on an object, then push that object to the array.
If I log the new object BEFORE pushing, I get the correct object values. If I log it from the array I get incorrect values only for reference.section. bridgedSection is fine, but reference.section matches across all entries in the array.
To say that I'm thoroughly flummoxed is an understatement. Can anyone shed some light on what I am (clearly) doing wrong?
var sectionCount = 0;
bridgedSections.forEach(bridged => {
var obj = potentialSections.find(obj => obj._id == bridged.referenceSection);
$ = cheerio.load(obj.html);
$(".meditor").html(bridged._id);// dropping the id here so it's easy to see if it was updated
obj.html = $.html();
obj.rand = Math.floor(Math.random() * 1000); // can't seem to add to obj either
var thisSection = {
referenceSection: obj,
bridgedSection: bridged,
}
console.log(thisSection) // correct value logged
currentSections.push(thisSection);
sectionCount++;
});
console.log(currentSections);
// this logs an array of the correct length but each
// {}.referenceSection is identical to the last entry pushed above
To try to clarify what both of the above folks are saying, the JavaScript language (like many others) has the concept of references, and makes very heavy use of that concept.
When one variable "refers to" another, there is only one copy of the value in question: everything else is a reference to that one value. Changes made to any of those references will therefore change the [one ...] underlying value (and, be reflected instantaneously in all of the references).
The advantage of references is, of course, that they are extremely "lightweight."
If you need to make a so-called "deep copy" of an array or structure or what-have-you, you can do so. If you want to push the value and be sure that it cannot be changed, you need to make sure that what you've pushed is either such a "deep copy," or that there are no references (as there obviously are, now ...) to whatever it contains. Your choice.
N.B. References – especially circular references – also have important implications for memory management (and "leaks"), because a thing will not be "reaped" by the memory manager until all references to it have ceased to exist. (Everything is "reference counted.")
And, all of what I've just said pretty much applies equally to every language that supports this – as most languages now do.
Javascript is passes function parameters by reference. This means the following happens:
derp = {a:1}
function passedByRef(param){
param['a'] = 2;
}
passedByRef(derp)
console.log(derp['a']) // 2
So when you pass a json object to a function, if you modify said object in the function it will change the original object. You probably want to make a deep copy of bridged before you assign it to thisSection because if you modify the version of bridged later on in thisSection it will modify the original object.
Here is a post that talks about cloning objects or you could look into something like immutable js
I think you need to look into Javascript deep copy.
You are modifying the original object when you modify the second assigned variable, because they are pointing to the same object. What you really need is to duplicate the object, not simply making a pointer to it.
Take a look at this:
https://scotch.io/bar-talk/copying-objects-in-javascript#toc-deep-copying-objects
I want to reduce the I/O overhead of fetching information from the server using XMLHttpRequest() or ActiveXObject(), as appropriate, by making a general-purpose function for doing the fetch which then stores the fetched data using sessionStorage.<variable>. The trouble is, I don't know in advance what the variable names all are, and I intentionally don't want to know.
I was thinking that if there is some way to "dereference" a variable, like we can easily do in BASH, for example, this would be trivial. For example, if the fetched data was in newData, and the name of the file it was fetched from is in dataFile, and if the dereference syntax was, say $(<variableName>), then one could write code like this to store and fetch the data:
//Store the data:
sessionStorage.$(dataFile) = newData;
//Fetch the data:
var storedData = sessionStorage.$(dataFile);
Get it? ... OK, now, how do I ACTUALLY do this?!
...The only other way around this I can see is VERY clumsy - make arrays, one with name, another with values - there MUST be an easier way! TIA.
As always, it is good to visit the docs first. There you can see, that sessionStorage (like localStorage or Map) has no properties at all (except a hidden property pointing to internal memory but thats another thing), but rather makes the data available through its get or set methods:
sessionStorage.set("a name", "a value");
However the session/localStorage provides also global getters/setters, so in theory one can do:
sessionStorage.name = "value";
If the keys name is dynamic one could use bracket notation:
sessionStorage[aName] = aValue;
However, they havent it even mentioned in the docs, so this feature is neither widely supported nor a good coding style.
Try this. Not exactly sure I followed your question clearly but I would try this if you are having issues using the variable name as string within your other code.
var_param = GET_YOUR_VAR_SOMEHOW; // strip your params as needed
let f1 = { getVarName:var_param.toString()} // object = to f2
let f2 = 'getVarName'; // string = to f1 key
var storedData = sessionStorage.$(f1[f2]); // result of f1[f2] should be your incoming var string, de-referenced from any content.
The Mozilla Dev Center says:
it is best not to add, modify, or remove properties from the object
during iteration, other than the property currently being visited;
there is no guarantee whether or not an added property will be visited,
However, I have no need to visit the added properties until later. So is it safe to add them?
E.g.
var animals = {"cats":25, "dogs":15}
for(var key in animals){
if(key.substring(0,3) !=="big"){ // no danger of referencing them
var newAnimal = "big" + key;
animals[newAnimal] =0;
}
}
Or will increasing the size of the object confuse the "for - in" iteration?
The docs don't say that the added propterties won't be visited, they say it's undefined.
So depending on the implementation, you may end up with a bigdogs, a bigbigdogs etc., resulting in an endless loop.
Or it may do something completely different, after all, it's undefined behavior.
To solve this, work with a copy of the object instead and add the new properties to the copy without mutating the looped object.
Edit: Looks like you are checking whether the key starts with big, missed that when I first looked at it. So you should be fine.
It is still good practice to avoid undefined behavior like this. This can easily come back and bite you when the code has to be changed at some point in the future and the reasoning behind the loop/check is not absolutely clear.
If you don't care that "there is no guarantee whether or not an added property will be visited", then you can do it.
If you want to make sure you won't visit them, make a snapshot of the properties before the loop:
var animals = {"cats":25, "dogs":15}
for(var key of Object.keys(animals))
animals["big" + key] = 0;
console.log(animals);
If you want to make sure you will visit them, use maps:
var animals = new Map([ ["cats",25], ["dogs",15]]);
for(var key of animals.keys())
if(key.slice(0,6) !== 'bigbig')
animals.set("big" + key, 0);
console.log([...animals].map(a => a.join(': ')));
Yes it is safe to add them since you have check to make sure you wont end up in an infinite loop.
The "add" adds it in random place so the order is not something you can count on, but looking as you don't need to visit the newly created property in the same loop this should be fine.
I would suggest if you just want to add new property create a new object which will later replace the current object once you are done. Because that way it will have better performance.
Also won't drive the person who is reviewing your code nuts :). Have a good day
It's safe
After start the iteration, when
delete an item before visiting , you not see it.
update an item, you see the new value
insert an item, you may or may not see it in the current cicle. But that was also safe.
source
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
at the moment I'm writing a small app and came to the point, where I thought it would be clever to clone an object, instead of using a reference.
The reason I'm doing this is, because I'm collecting objects in a list. Later I will only work with this list, because it's part of a model. The reference isn't something I need and I want to avoid having references to outside objects in the list, because I don't want someone to build a construct, where the model can be changed from an inconsiderate place in their code. (The integrity of the information in the model is very important.)
Additional I thought I will get a better performance out of it, when I don't use references.
So my overall question still is: When should I prefer a clone over an reference in javascript?
Thanks!
If stability is important, then clone it. If testing shows that this is a bottleneck, consider changing it to a reference. I'd be very surprised if it is a bottleneck though, unless you have a very complicated object which is passed back and forth very frequently (and if you're doing that it's probably an indication of a bad design).
Also remember that you can only do so much to save other developers from their own stupidity. If they really want to break your API, they could just replace your functions with their own by copying the source or modifying it at runtime. If you document that the object must not be changed, a good developer (yes, there are some) will follow that rule.
For what it's worth, I've used both approaches in my own projects. For small structs which don't get passed around much, I've made copies for stability, and for larger data (e.g. 3D vertex data which may be passed around every frame), I don't copy.
Why not just make the objects stored in the list immutable? Instead of storing simple JSON-like objects you would store closures.
Say you have an object with two properties A and B. It looks like that:
myObj = {
"A" : "someValue",
"B" : "someOtherValue"
}
But then, as you said, anyone could alter the state of this object by simply overriding it's properties A or B. Instead of passing such objects in a list to the client, you could pass read-only data created from your actual objects.
First define a function that takes an ordinary object and returns a set of accessors to it:
var readOnlyObj = function(builder) {
return {
getA : function() { return builder.A; },
getB : function() { return builder.B; }
}
}
Then instead of your object myObj give the user readOnlyObj(myObj) so that they can access the properties by methods getA and getB.
This way you avoid the costs of cloning and provide a clear set of valid actions that a user can perform on your objects.
I've got an array, and it's got a method I threw onto it called add, which I use as a wrapper around push. I've found myself using push a few times when I should have used add, and now I'm thinking it would be nice to assign a reference to my add method to the array's native push. Thus, calling push on the array would call add.
Do internals depend on externally available native methods like push? How would this affect compatibility? Is this a bad idea? If so, why?
Some code:
PR.state = {
views: []
};
_.extend(PR.state.views, {
add: function(view) {
var parent = view.parent;
if ((!this.length && view instanceof PR.Views.App) || (parent && _.contains(this, parent)))
this.push(view);
}
});
// I am thinking:
PR.state.views.push = PR.state.views.add;
I would strongly advise against changing the behavior of a standard array method. If you really want a custom method, then just create a new method and give it it's own unique name and use that.
Changing the behavior of existing methods could have all sorts of bad consequences:
Incompatibility with code retrieved from any other source.
Creates a non-standard and unexpected implementation if anybody else ever works on this project. This is like adding in a time bomb to trip up some future developer.
Training yourself to use your own custom method instead of .push() is just something that a decent developer would do. Just do it.
Creating a newly named method with an appropriate and descriptive name improves the readability, understandability and maintainability of your code. Replacing an existing method with something that works differently does the opposite.
It's not so bad if you just replace the method on one instance of an array, not the whole array prototype, but it's still not a good idea.
What a stupid question. If I replace push with add, then what happens when I call push from add? :< :< I haven't tested it, but I suspect that while Array.prototype.push will still be available, unless I use Array.prototype.push explicitly, calling add will result in a mondo endless loop.