Javascript array weird behavior - javascript

Ok, when write like below:
var element = { "name": "" };
var array = [];
for (var i = 0; i < 2; ++i) {
var newelement = element;
newelement.name = i.toString();
array[i] = newelement;
}
Result in:array[0].name == array[1].name == "1".
But write in another way:
var element = { "name": "" };
var array = [];
for (var i = 0; i < 2; ++i) {
var newelement = { "name": i.toString() };
array[i] = newelement;
}
Result in:array[0].name == "0" and array[1].name == "1".
Tell me why.

Because in the second example you are creating a new object on each iteration, but in the first example you are referencing always the same element.

This is a good Javascript question for people to encounter and understand.
In the first block of code, you are assigning newelement a reference to element. Each time through the loop, newelement gets the same reference assigned to it. You aren't creating any new objects, just assigning the same one over and over again.
In the second block of code, you are creating a new object inside the loop so each assignment goes to a different object.
You need to remember that in javascript, assigning an object to a variable only assigns a reference to that object.
var newelement = element; // just assigns a reference to an existing object
Yet, assigning like this is creating a new object:
var newelement = { "name": i.toString() }; // creates a new object
So, in the first code sample, you have array[0] and array[1] each with a reference to the same object. When you modify that object, it affects both array[0] and array[1] since they are both pointing at that object.
In the second code sample, array[0] and array[1] are each pointing at a different object so when you modify one, it does not affect the other.
This is a tricky part of javascript and is something that often trips up C/C++ programmers (it certainly got me when I was first learning JS) who are use to something like the first assignment being a structure copy. Javascript defaults to just assigning a reference unless you're using syntax that specifically creates a new object.

Related

last index array without loop

Good morning/afternoon
I Want to create an object whitch simulate an array, for new browsers no pb I change the proto of an array with mine but for oldest version of IE i need to know the length of my false array by finding the last index.
I have found a way for IE:
var getLastPos = function(){
for( var i in falseArray){
//do nothing
}
return i
}
but if a faster way exists, it rather like. I try to pass regex to lastindexof
but it seems don't work
thanks.
when you want to find the last index from the array, use
<array>.length
So you do not need the for loop, and your function, use
falseArray.length
If you need the value from the last position use something like this:
falseArray[falseArray.length-1]
//decrement by one, because the index from the array starts by 0
I hope this is helpful.
Thks for you reply,
It was a old question, but I will answer, my goal was to create a false array in order to not polluate prototype.
By false array I mean an object who have the same behavior than a array with .length property and the use of '[]'.
My conclusion is that it is only possible at the class level with new Browser that allow to modify the proto
Ex:
var myClassArray = function(){
var obj = []
obj.__proto__ = myClassArr.prototype
return obj
}
myClassArr.prototype = Array;//inheritance
myClassArr.prototype.last = function(){
return this[this.length-1]
}
or for old browser at object level
var myClassArray = function(){
var obj = []
obj.last= function(){
return this[this.length-1]
}
return obj
}
how to use it:
var myArray = new myClassArray;
console.log(myArray.length) //0
myArray[5]=1;
console.log(myArray.length) //6
console.log(myArray.last()) //1
Conclusion:
Modifiying the proto is a bad idea about perf and old browser.
you can improve the code for old browser by creating function outside the class constructor else browser will duplicate function.
var last = function(){
return this[this.length-1]
}
var myClassArray = function(){
var obj = []
obj.last = last
return obj
}
var a = new myClassArray
var b = myClassArray()//In fact new is facultative
b.last == a.last //same ref no duplication

Unexpected result pushing objects into an array

How to iterate over an array of literal objects in JavaScript?
I would like to do something like that:
grupo = []; // declare array
text = {}; // declare new object
text.a = "texta"; // declare property "a" of an object.
text.b = "textb";
grupo.push(text); // add object to array
text = {}; // declare new object
text.a = "textc"; // declare property
grupo.push(text); // add object with other property
// Iterate over
for (i=0; i<=grupo.length; i++) {
console.dir(grupo[i].text.a);
}
There are various errors in that code:
You're putting the same object in the array twice, not putting two objects in the array. After you push text into the array, you're just overwriting the a property on the same object and pushing it again. You haven't created a new object.
You haven't declared any of your variables (everywhere you've said "declare" in your comments, those are not declarations), so you're falling prey to The Horror of Implicit Globals. Use var to declare variables.
A line comment should start with //, not \\ (those cause a syntax error)
The for loop at the end should use <, not <=, for its termination condition. For the various ways to loop through arrays in JavaScript, see this question and its answers.
Here's a cleaned-up version of that code:
var text, grupo, i; // Declare variables
text = {}; // Create an object and assign it to the variable
grupo = []; // Create an array and assign it to the variable
text.a = "texta"; // Set the property `a` on the object
text.b = "textb"; // Set the property `b` on the object
grupo.push(text); // Put that object onto the array
text = {}; // Create a second object
text.a = "textc"; // Set the property `a` on that new object
grupo.push(text); // Put that object on the array
for (i=0;i<grupo.length;i++) {
// ^-------------------- Note no =
console.dir(grupo[i].text.a);
}
Do you mean something like this?
for (var key in validation_messages) {
var obj = validation_messages[key];
for (var prop in obj) {
// important check that this is objects own property
// not from prototype prop inherited
if(obj.hasOwnProperty(prop)){
alert(prop + " = " + obj[prop]);
}
}
}
Reference: https://stackoverflow.com/a/921808/1054926
groupo[i] is already a text object so you there is an error there. Also, you don't want to look until your index is <= to the length.
Here is a quick look at what you may be looking for in your loop:
for (i=0;i<grupo.length;i++) {
console.log(i,grupo[i].a);
}
However you will have additional problem when you discover that the value of "a" is not what you may be expecting.
Here another possible "solution"
var text = {};
var grupo = [];
text.a = "texta";
text.b = "textb";
grupo.push(text);
text.a = "textc";
grupo.push(text);
for (var i=0;i < grupo.length;i++) {
var x = grupo[i];
if (x && x.a){
console.log(x.a);
} else {
console.log(x);
}
}

use object name in javascript loop of array

I want to loop through an array of objects, check if the name of the object variable is equal to the id of an element passed to the function, and if so set the innerHTML of another object to the name property of the matching object.
ex.
var samplearray = new Array();
var Guy1 = new Object();
Guy1.name = "Bill";
Guy1.health = 100;
samplearray.push(Guy1);
Guy2.name = "Dan";
Guy2.health = 125;
samplearray.push(Guy2);
//this is all done previously by a function on pageload
function afunction(id){
for (item in samplearray)
{
if (item == id.id){
document.getElementById("changeme").innerHTML=samplearray[item].name;
}
}}
"item" in the if doesn't seem to refer to the name of the variable. If i check it with a custom var_dump function, it tells me the value is "11" and not "Guy1". I have no idea why.
edit:
the fixed for loop:
for (var item in samplearray)
{
if (samplearray[item].varname == id.id){
document.getElementById("changeme").innerHTML=samplearray[item].name';
}}
I don't quite understand what you're doing, but here's some comments on your code:
> var samplearray = new Array();
> var Guy1 = new Object();
> Guy1.name = "Bill";
> Guy1.health = 100;
> samplearray.push(Guy1);
> Guy2.name = "Dan";
> Guy2.health = 125;
> samplearray.push(Guy2);
It is considered better style (and a bit tidier) to use object and array initialisers*:
var guy1 = {name: "Bill", health: 100};
var guy2 = {name: "Dan", health: 125};
var samplearray = [guy1, guy2]
Also, variable names starting with a capital letter are, by convention, reserved for constructor functions.
> //this is all done previously by a function on pageload
You need to wait for elements to be available before interacting with them, waiting for the load event is one way of doing that.
> function afunction(id) {
What is id? You seem to treat it like an object later.
Ah, so id is a reference to an element, and id.id should return the element id.
> for (item in samplearray) {
You should declare variables so they do not become globals, so:
for (var item in samplearray) {
It's generally not a good idea to use for..in with an array because the order that members are returned may be different to their index order. Also, it will return all enumerable properties, including those on the prototype chain so you should guard against that if it's not what you want. Much better to use a for loop so you can guarantee the order and avoid non–numeric enumerable properties:
var item;
for (var i=0, iLen=samplearray.length; i<iLen; i++) {
item = samplearray[i];
> if (item == id.id){
So item will be a reference to an Object member of samplearray and id.id is a string so this will always return false, none of the following code will run.
> document.getElementById("changeme").innerHTML=item.name;
In the for..in version, item is a string property name, but you are treating it like an object so this will throw an error and script execution will stop.
In the for loop version, it's a reference to one of the objects in samplearray so the above should "work".
> document.getElementById("changeme").innerHTML=samplearray[item].name;
This should have worked provided item was a numeric property name and not some other enumerable property.
> //neither does this
> } }}
* Intialiser is a general term for an expression that creates an object (such as an Object, Array, Regular Expression, etc.). Where the initialiser uses literal values it may be called a "literal".
var Guy1 = new Object();
In this statement, the Guy1 object has no reference to the string "Guy1". The object exists without the variable. Indeed, the next statement could say:
var friend = Guy1;
and the object Guy1 object would be unchanged.
Besides this, I think you are getting confused about how the for...in loop works. Try reading more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
If you really want your Guy1 object to have a property of "Guy1", you will need to assign it as such:
Guy1.varname = 'Guy1';
And then you can check if (item.varname == id.id) once your for...in loop works correctly.

JavaScript variable assignment?

Looking at the following code, can someone explain how values are passed around in JavaScript.
function loadImages() {
for(var sec in images) {
theme = images[sec];
for(var tsec in theme) {
theme[tsec].img = new Image();
theme[tsec].img.src = 'images/'+theme[tsec].src+'.png';
}
}
}
Then in another functions:
function definitionToSpriteDataMapping() {
var result = {};
for(var definition in blocks) {
var sprite = blocks[definition].sprite;
for(var secnm in images) {
section = images[secnm];
for(var spritenm in section) {
if(sprite == spritenm) {
result[definition] = {};
result[definition].img = section.img;
}
}
}
}
return result;
}
I cut out some code for simplicity sake but its still quite convoluted. Basically there are 2 objects (images & blocks) which are nested key:value pairs. In the first block of code
theme = images[sec];
theme[tsec].img.src = 'images/'+theme[tsec].src+'.png';
In the second line of code there is
section = images[secnm];
result[definition] = {};
result[definition].img = section.img;
There is no .img in "images" before the first block of code where .img is added to "theme". But this seems to be reflected back into "images" as seen in the second block of code. Are all objects like pointers in JavaScript? Will "result" have the same relationship with "blocks" as "theme" has with "images"? What if I remove an element from "theme", will that be reflected in "images"?
Using theme = images[sec] you will indeed create a pointer to that object in memory. So adding img to theme object will as well add img to that image, as they are the same object. So yes, the same goes for result.
Altering, adding or removing properties of an object referenced in such a way will influence the actual object. The same goes for arrays.
If you don't like that behavior, you should clone the object. You can clone a simple object simply by copying all properties:
var original = { name: "James", age: 73, male: true };
var clone = { };
for( var k in original )
clone[ k ] = original[ k ];
But if any property of that original is an array or object itself, it will be a reference. If you don't have any objects or arrays as properties, the above snippet will do fine. Otherwise you should write a clone function and recursively clone every member of the original.
Are all objects like pointers in JavaScript?
Effectively, yes, though I believe it would be more generally stated that in JavaScript an Object is a "reference type".
If var a references an object, and a is assigned to var b, b will get a copy of the reference that a holds, so a and b will both reference the same object data in memory.
Changes made from the a reference are observable from the b reference.
Note that this is still a "by value" assignment, but the value copied is the value of the reference, not the object itself.
The reason you are experiencing this is that objects are passed by reference. There are ways to clone objects. Take a look at this other SO post How do I correctly clone a JavaScript object?
You're altering images[sec][tsec] in both cases, which refers to the very same object in memory. Just doing theme = images[sec] does not make a copy of the object.
A more trivial example of this behaviour is this:
var obj = {};
var obj2 = obj;
obj.a = 123;
obj2.a; // 123

Remove element from Javascript associative array using array value

I am trying to remove an element from a Javascript associtive array using the value to find it, but I am having trouble. I have tried splice and JQuery's grep method and neither have worked for me. This is what I currently have.
var array_path = new Array();
function bulk_upload(){
var temp_array = new Object();
for (var i = 1; i<8; i++){
temp_array[i] = $('#path' + i).val();
if(temp_array[i]!='' && temp_array[i]!=null){
array_path['path' + i] = $('#path' + i).val();
}
}
process_txt();
}
function process_txt(){
//alert(array_path.indexOf(full_path)); //returns nothing
var removed_element = array_path.splice(getKey(array_path), 1);
//array_path = $.grep(array_path, function(val) { return val != full_path; });
alert(removed_element);//return nothing, just blank alert box
}
function getKey(data) {
for (var prop in data)
return prop;
}
The way to do this is to use the delete operator.
delete array_path[getKey(array_path)]
Some Background Information
In JavaScript, almost everything descends from Object.prototype. JavaScript, being an open and dynamic language allows you to create/modify properties of objects by simple assignment. This is very similar to what an associative array -- a structure that contains keyed values.
Under the hood an array is just an object that descends from Array.prototype with numeric keys and a special property called length. The length property just returns one greater than the highest numeric property. In essence, an Array is an object with different semantics.
If you're wanting an associative array then Array is not the object you want to descend from. You would want to descend directly from Object. There are two ways to do that, you could either use the new operator or an empty object literal. The syntax for both is below:
var o = new Object();
var o = {};
The second is preferred since it's a little bit more concise.
I wrote a blog post about this a while back, have a look if you want a little bit more info.
There is no such thing in JavaScript as an "associative array" per se. The data structure which corresponds to this concept is simply a JavaScript Object.
Of course, a JavaScript Array (like essentially everything in JavaScript) is an Object, but one with additional capabilities. So you can use an Array as a key-value map, but it's really not the correct structure for that.
To remove a key from an Object, you just do something like this:
var myObj = {};
var myKey = "blah";
myObj[myKey] = 1234; // Adds or updates value for "blah" to 1234.
delete myObj[myKey]; // Removes key-value pair for "blah".
Have you tried delete hash.someKey; ?
You can give your object a remove method, or use apply or call to use another object's remove method, if defined.
function myObj(members){
for(var p in members) this[p]= members[p];
}
myObj.prototype.remove= function(val){
for(var p in this){
if(this[p]=== val) delete this[p];
}
return this;
}
myObj.prototype.toString= function(){
var A= [];;
for(var p in this){
if(this.hasOwnProperty(p)){
A.push(p+':'+this[p])
}
}
return '{'+A.join(', ')+'}';
}
var O= new myObj({a: 1, b: 10, c: 100});
alert(O)
O.remove(10);
alert(O)
I'm not psychic, so I can only guess that you wanted to accomplish something like this:
var paths = [];
function getPaths() {
for(var i = 1; i < 8; ++i) {
var value = $('#path' + i).val();
if(value) paths.push(value);
}
}
function process() {
var firstPath = paths.shift();
// do stuff
}
getPaths();
if(paths.length) process();

Categories