Global array with jquery event - javascript

The code is the following:
$('input').click(function(){
window.array[$(this).attr('id')]=true;
console.log('Input id: '+$(this).attr('id')+' - Num:'+window.array.length+
' - Array value:'+window.array[$(this).attr('id')]);
alert(window.array.join('\n'));
});
The behavior is very strange: when the event is fired, in the console I can read Input id: example_id - Num:0 - Array value:true, then in the alert I get undefined (empty array? but the console told me the correct value!).
When I click on another input, exactly the same thing happens, such as the previous event hasn't been fired. It seems that the callback function creates an array every time it's called, but I'm using window object, and I was told that attaching arrays to window object is the same of global vars!
Can you explain me this strange behavior?

The length property of an array is its largest numeric property plus one (or zero if it has no numeric properties). So setting non-numeric properties on an array does not change its length.
join only effects numeric properties as well. Still, the result should be an empty string, not undefined. Are you sure you are using a normal array?

Your situation demands that you use an object since you need to use a string as your "id".
if you have defined:
window.array = [ ]
and write:
window.array["id1"] = true; // WRONG!
The above simply means that you'll get:
window.array["id1"] //true but window.array is still [ ]
which is because window.array, though an array is still an object in JavaScript and you can add properties to it but the usage is wrong. To add to an array, you need integers(or integers in the form of string) as indexes.
window.array[1] = true; // OR window.array["1"] = true (!! even this works)

Related

Why does the isArray() Javascript method in this example returns true, if the array has been redefined as a standard object?

I'm trying to learn Javascript - here's my issue:
In the w3schools.com javascript array examples, they show the sequent example:
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
document.getElementById("demo").innerHTML =
person[0] + " " + person.length;
An array "person" has been defined, but then they proceed to add some elements whit a "named" index. Then tries to print the HTML document the 0th element and the number of elements of the array, like you would do with a standard array.
The description says:
If you use a named index when accessing an array, JavaScript will
redefine the array to a standard object, and some array methods and
properties will produce undefined or incorrect results.
In fact, person[0] and person.length return respectively "undefined" and "0". Even is person was initially defined as an array, by inserting new named indexes elements, the array should be redefined as an object. But when i try do use the Array.isArray() method for checking it, it returns true:
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
document.getElementById("demo").innerHTML =
person[0] + " " + person.length;
document.getElementById('test').innerHTML = Array.isArray(person);// returns true
So, why? if, as specified by the tutorial, this has been effectively redefined as a standard object, and the ECMAScript 5 has added the .isArray() method for checking if something is an array and nothing else, shouldn't this return false insted of true?
I'm sure i missed something. If i define person like this:
person = {};
then it returns false, as expected. What is happening here? I just wanted to understand arrays a little bit more, this confuses me. Is this just a broken array, but still an array?
Here's the example (without the Array.isarray() bit, just the default): https://www.w3schools.com/js/tryit.asp?filename=tryjs_array_associative_2
First of all I want to note that the example you took from the w3schools page on arrays, is from the "Associative Arrays" section, which has this important introduction:
Many programming languages support arrays with named indexes.
Arrays with named indexes are called associative arrays (or hashes).
JavaScript does not support arrays with named indexes.
In JavaScript, arrays always use numbered indexes.
This puts the example into context, because it really makes no sense to define a variable as an array and then use string keys. But this was an example to illustrate the point.
Does an Array become an Object?
That JavaScript still considers the variable to be an array is as expected. It becomes an array at the moment of assignment of [], and that does not change by adding properties to that object. Yes, arrays are objects. They just have additional capabilities.
The array did not lose any of its array-like capabilities, but those features just don't work on those string properties, ... only on numerical ones (more precisely, the non-negative integer ones).
You loosely quoted the following statement from w3schools:
If you use named indexes, JavaScript will redefine the array to a standard object.
That is wrong information and leads to your misunderstanding. There is no redefinition happening. When you add properties to any object, then the object does not change "type". It remains an instance of what it was before... An array remains an array, a date object remains a date, a regex object remains a regex, even if you assign other properties to it. But non-numerical properties do not "count" for an array: the length will remain unchanged when you add such properties. The length only reveals something about the numerical properties of the object.
This quote is yet another illustration of what the JavaScript community thinks about w3schools.com, i.e. that it is not the most reliable reference, even though it has its value for learning the language.
Example of adding useful properties to arrays
Having said the above, there are cases where you may intentionally want to make use of such properties on arrays. Let's for example think of an array of words that is sorted:
const arr = ["apple", "banana", "grapefruit", "orange", "pear"];
Now let's add something to this array that denotes that it is currently sorted:
arr.isSorted = true;
We could imagine a function that would allow one to add a value to this array, but which also verifies if the array is still sorted:
function addFruit(arr, fruit) {
if (arr.length && fruit < arr[arr.length-1]) {
arr.sorted = false;
}
arr.push(fruit);
}
Then after having added several values, it would maybe be interesting to verify whether the array needs sorting:
if (!arr.sorted) arr.sort();
So this extra property helps to avoid executing an unnecessary sort. But for the rest the array has all the functionality as if it did not have that extra property.
An object that is set up as an array and then filled as an object becomes a member of both classes. Methods of the Array class will apply to its 'array-ness':
Array.isArray(person);
returns true. Methods of the Object class will apply to its 'object-ness':
typeof(person);
returns object. When it could be either one, the 'array-ness' will prevail, because the variable was first defined as an array:
console.log(person);
will put Array [ ] on the console, because it runs the Array class's logging method. It is displayed as an empty array, since it has no numbered elements, but you could add some:
person[2]=66;
and then console.log would log Array [ <2 empty slots>, 66 ].
I think the polyfill implementation of isArray() will clear your doubt by some extent.
#Polyfill

What is falsy here ? Why does it dedupe? and why does console.log() contain the answer when infront of the foreach?

Q1-Why does this message print with the object even though it is before the forEach?
Q2-Why does this dedupe the new object
Q3-What triggers the falsy here that allow the {} to be added?
var test = [{id:1,name:'s1'},{id:2,name:'s2'},{id:2,name:'s2'},{id:1,name:'s1'},{id:4,name:'s4'}];
var test1 = {};
//Q1
console.log(test1);
test.forEach(function(item){
//Q2 and Q3
var o = test1[item.name] = test1[item.name] || {};
o.id = item.id;
});
<!--Output
{}
​
s1: Object { id: 1 }
​
s2: Object { id: 2 }
​
s4: Object { id: 4 }
​
__proto__: Object { … }
--!>
The console is tricky. It does NOT lie, it just stores a reference to that object. It's not "taking a picture" of the object as it is when you log it, it's actually reflecting the object itself. If you were to update it on click, it would update that reference in the console, too. If you alert it, you would get [object Object] instead of the text. You can use console.log(JSON.stringify(test1)) to print the contents as they were at the moment the object was read in the code.
As for deduping, it's pretty straight-forward, but a bit of a logic puzzle. During one iteration, for example, it will see test1['s1'] and assign it to 's1'. The next time it runs across another s1, it's actually referencing the same property as before, test1['s1'] and reassigning it to 's1' again.
I'll come back to take a closer look at question 3, but it's a good idea to ask one clear question at a time here on StackOverflow. ;)
--
Edit: Well, I'm not exactly sure about how you're getting those values in your log to be honest, because I'm not getting the same results even though I'm running the same script. Check my code on codepen to see my script working as expected!
Q1. As discussed in comments, major browsers now log an inspectable tree of an object, not the conversion of the object to a string using its toString method. The tree is based on a reference to the object and may show property values current when expanding the tree rather than when the object was logged.
Q2. The forEach loop sets properties of test1 using item.name values for items in the test array. If name values are repeated in test entries, only the one property name is updated in test1. Hence the de-duplication - an object can't store separate properties of the same name.
Q3. Initialisation of a test1 property to an empty object only occurs the first time a property is created in test1 for a property name held in item.name. Subsequently, if the same value of item.name is encountered again, it retrieves the pre-existing property from test1 because
test1[item.name] || {};
now evaluates to the existing test1 object property. (If the left-hand operand of an || operator is non falsey, it returns the left-hand operator value as the result of the operation.)
That perhaps leaves the o variable - it is a copy of the reference to an object stored in test1. Updating its id property updates the same object as held in test1. If test had multiple entries of the same name but different id values, the test1 property for the name would hold the id of the last duplicate entry in test.

JavaScript arrays: string indexed items

I've had a bit of a wakeup to the nature of JavaScript array indexes recently. Pursuing it, I found the following (I'm working with Node.js in interpretive mode here):
var x=[];
x['a']='a';
console.log(x); // Yields [ a: 'a' ]
console.log(x.length); // yields 0 not 1
x[1]=1;
console.log(x); // Yields [ , 1, a: 'a' ]
console.log(x.length); // Yields 2 not 3 (one for empty 0 space, one for the occupied 1 space)
Is a: 'a' really what it looks like - an object property embedded in an array - and, thus, isn't counted in the array property .length?
In JavaScript, arrays are just objects with some special properties, such as an automatic length property, and some methods attached (such as sort, pop, join, etc.). Indeed, a will not be counted in your array, since the length property of an array only stores the amount of elements with a property name that can be represented with a 32-bit positive integer.
And since arrays always automatically define every numbered element up to the highest element with a positive 32-bit int property name, this effectively means the length property stores 1 higher than the element with the highest 32-bit integer as a property name. Thanks #Felix Kling for correcting me about this in the comments.
Adding properties such as a is not forbidden at all, but you should watch out with them, since it might be confusing when reading your code.
There's also a difference in walking through the elements in the array:
To walk through all the numbered elements:
for (var i=0; i<myArray.length; i++) {
//do something
}
To walk through every property that's not built-in:
for (var i in myArray) {
//do something
}
Note that this loop will also include anything that's included from Array.prototype that's not a built-in method. So, if you were to add Array.prototype.sum = function() {/*...*/};, it will also be looped through.
To find out if the object you're using is indeed an array, and not just an object, you could perform the following test:
if (Object.prototype.toString.call(myObject) === '[object Array]') {
//myObject is an array
} else if (typeof myObject === 'object') {
//myObject is some other kind of object
}
See #artem's comment: myObject instanceof Array might not always work correctly.
That's correct, and it's a good illustration of the fact that that new Array you've created is really just a special kind of Object. In fact, typeof [] is 'object'! Setting a named property on it (which might be written more cleanly here as x.a = 'a') is really setting a new property to the object wrapper around your "real" array (numbered properties, really). They don't affect the length property for the same reason that the Array.isArray method doesn't.
Yes, that's correct. This works because pretty much everything in JavaScript is an object, including arrays and functions, which means that you can add your own arbitrary string properties. That's not you say that you should do this.
Arrays ([]) should be indexed using nonnegative integers. Objects ({}) should be "indexed" using strings.

Elements in Javascript object coming out undefined

To construct an object like this:
params (Object) (defaults to: {}) —
Delete — required — (map)
Objects — required — (Array<map>)
Key — required — (String) Key name of the object to delete.
I'm doing this:
var params = {
Bucket : data.bucket,
Delete: [{
Objects:[{ Key:""}] }]
};
for(i=0;i<data.keys.length;i++){
params.Delete[0].Objects[i].Key = data.keys[i];
}
It breaks on the Key with
params.Delete[0].Objects[i].Key = data.keys[i];
^
TypeError: Cannot set property 'Key' of undefined
What am I doing wrong?
It turns out there is only one Key inside of Objects. I'd like to put as many keys as I want inside of Objects so as to properly construct the object. How can I do this?
How many object literals are in the Objects array? From your initialization it only looks like 1. But you are using a for loop, implying you expect more than one.
That error means Objects[i] is undefined for whatever value of i you reach when it occurs.
It seems like you need to do something like
for(i=0;i<data.keys.length;i++){
params.Delete[0].Objects.push({Key: data.keys[i]})
}
to get new object literals into your Objects array as you process your data (change your definition of params to just have an empty array for Objects initially).
Your params.Delete[0].Objects here has only one object in it and it happens when the i is 1. you are counting a for loop based on the other loop with 2 different length.
data.keys
is different from:
params.Delete[0].Objects
this one has only one object, whereas the first one has more than one object.

Odd Javascript alert output

I have an odd problem. I'm trying to use Javascript to fetch me some values from a multidimensional array, and it's giving me some weird output.
Here is my code:
foo = [['3','4'],['5','6']];
for (bar in foo) {
baz = bar[0];
alert(baz);
qux = bar[1];
alert(qux);
}
Here is the output of the above:
// These are all alerts, by the way
0,undefined,1,undefined,$,f,$,c,e,a,c,l,c,l,i,i,n,a,s,l,i,c,o,a,p,g,e,g,e,i,n,c,o,e,r,e,m,f,l,p,i,h,e,r,g
Can somebody tell me what is happening?
Here is a jsFiddle of the problem: http://jsfiddle.net/Jey6w/
Edit:
Here is another jsFiddle, with another layer of "Inception": http://jsfiddle.net/8vyGq/
The output:
// Again, these are all alerts, and * signifies undefined
0**1**$ff$ceaacllcllinnassliicooappgeegeeinncooerremmfllpiiheergg
The JavaScript for ... in loop gives you the names of the object properties, not the values.
Don't use for ... in for real arrays. Use a numeric index or .forEach().
The reason you're getting your output is complicated and uninteresting, since you just shouldn't do that, but here's a start. The property names will be coerced to strings by the for ... in. Thus, on the first iteration, "bar" is the string "0", so ("0")[0] is just "0", and ("0")[1] is undefined because "bar" is a single-character string.
After that, your for ... in loop staggers into some other properties inherited from somewhere; perhaps you're using Prototype or something. The loop then alerts the first two characters of the names of all those other properties.
I could be wrong, but I think it's due to the fact that bar is returning a reference to a property within an object. Changing your selectors to foo[bar][0] works a treat.
foo = [['3','4'],['5','6']];
for (bar in foo) {
alert(foo[bar][0]);
alert(foo[bar][1]);
}​
In cases where your object is simply a multi-dimensional array, I would sway array from using the for in statement, as it can select unwanted properties. I would stick to the good old fashioned for(start, stop, increment)
foo = [['3','4'],['5','6']];
for (i = 0; i < foo.length; i++) {
alert(foo[i][0]);
alert(foo[i][1]);
}​
Update - jQuery
As there has been mention of jQuery's .each method I thought I'd also post an example of how it could be utilised. The jQuery's each method passes 2 optional parameters, indexInArray, and valueOfElement. Additionally, the jQuery documentation also states that
The value can also be accessed through the this keyword, but
Javascript will always wrap the this value as an Object even if it is
a simple string or number value
With this in mind, we could achieve the same results as previous example, using the following jQuery (jsFiddle):
var foo = [['3','4'],['5','6']];
$.each(foo, function() {
// 'this' is the context of the current item within the array
alert(this[0]);
alert(this[1]);
}​)​

Categories

Resources