I have some javascript as shown below
for (var titleKey in data.d) {
var title = data.d[titleKey];
}
This is actually coming back from a JQuery call to a .NET webservice but I don't believe that's related.
My loop iterates over each element in the collection correctly, it then continues through the loop one more time. The titleKey here is 'indexof' and title is 'undefined'.
This is happening in two different places in my code.
What is causing this? How can I prevent it?
Thanks in advance.
You need to exclude from the loop the properties of the prototype. The for ... in structure will loop through everything* it finds in the prototype chain, not only the properties of the child object.
for (var titleKey in data.d) {
if (data.d.hasOwnProperty(titleKey)) {
// own property //
}
else {
// inherited property //
}
}
From what the console log says my suspicion is that you have a library that implements the indexof for Array in its prototype.
My recommendation would be to use the correct way to walk Arrays:
for (var index = 0; index < data.d.length; index++) {...}
for in is for Objects, not Arrays. This is a common beginner mistake, where one abuses the fact that Array is derived from Object.
* See comment from davidchambers
Javascript's "for-in" iterates through all properties of an object, and this includes method names.
Use the following loop mechanism:
for (var i = 0; i < titleKey.length; i++) {
var title = data.d[titleKey[i]];
}
The for:in loop loops through all properties of an object, rather than just ones that are indexable.
If indexof comes from the prototype chain, you must use hasOwnProperty to skip over it.
for (var titleKey in data.d) {
if (data.d.hasOwnProperty(titleKey))
{
var title = data.d[titleKey];
}
}
Based on the information at hand, I'd say that data.d.indexof is in fact undefined. The following is a perfectly valid data structure:
foo: 42
bar: [1, 2, 3]
baz: false
indexof: undefined
As a sanity check, try the following:
console.log(Object.prototype.hasOwnProperty.call(data.d, 'indexof'))
Edit: Now that it has become clear that the data structure is in fact an array, this answer does not solve the OP's problem. It's still valid, though, so I won't remove it.
Related
So I am learning Javascript and testing this in the console.
Can anybody come up with a good explanation why something like this does not work?
var students = ['foo', 'bar', 'baz'];
function forEach2(arr1) {
for (i = 0; i < arr1.length; i++) {
console.log(arr1[i]);
}
}
students.forEach2(students)
I get : Uncaught TypeError: students.forEach2 is not a function
For me it would seem logical to work but it doesn't
why?
forEach2 is not a function of students. students is an array containing 3 string values. just use forEach2 without students. before it.
var students = ['foo', 'bar', 'baz'];
function forEach2(arr1) {
for (i = 0; i < arr1.length; i++) {
console.log(`arr1[${i}]:`, arr1[i]);
}
}
console.log("students:", students);
console.log("students has .forEach2 function ?", typeof students.forEach2 == "function");
console.log("forEach2 is a function?", typeof forEach2 == "function");
console.log("forEach2(arr1)...");
forEach2(students);
console.log("students.forEach(student)...");
//forEach already has a native implementation
students.forEach((student)=> {
return console.log("student:", student);
});
JavaScript is an object-oriented language with prototypal inheritance.
Object-oriented means you have objects with members (called properties in JavaScript) which hold values. In JavaScript, functions are “first-class citizens”, meaning you can assign them to variables just like you would other values.
When you write write a function such as ‘function x(y) { return y +1; }’, what’s really happening is 1) you are declaring a variable named “x”, and 2) you are creating a function “value” which is then assigned to that variable. If you have access to that variable (it’s within scope), you can invoke the function like ‘x(5)’. This evaluates to a new value which you could assign to another variable, and so on.
Ok, so now we have a problem. If functions are values, and values take up space (memory), then what happens when you need a bunch of objects with the same function? That’s where prototypal inheritance comes in. When we try to access a value on an object, via either the member access operator ‘.’, like ‘myObj.someValue’, or via an indexing operator ‘[]’ like ‘myObj[“someValue”] (both of which are equivalent in JavaScript, for the most part), the following occurs:
The runtime checks to see if a ‘myObj’ variable exists in the current scope. If it doesn’t? Exception!
The runtime looks at the object referenced by the variable and checks to see if it has a property with the key “someValue”.
If the object has that property, the member access expression (‘myObj.someValue’) evaluates to that property’s value, and we’re done.
If the object does not have that property, we start doing prototypal inheritance stuff. In JavaScript, all this means is that when we try to access a property that doesn’t exist, the runtime says “hey, what if this objects *prototype has a property with that key?” If it does, we use the prototype’s property’s value. If it doesn’t, we return ‘undefined’.
Notice that because prototypes are just objects, and therefore can themselves have a prototype, step 4 is recursive until we run out of prototypes on which to attempt member access.
Ok, so at this point you may be thinking “what’s a prototype and what does it have to do with my question?” It’s just an object. If any object has a property with the key “prototype”, then that property’s value IS a prototype. Specifically, it’s that object’s prototype. And so this is where your problem arises.
There is no property on your object with the key “forEach2”. Why? Because you didn’t put it there, and you didn’t put it on the object’s prototype (or any of the prototypes “up stream”.
The ‘forEach’ function of an Array exists as a property on the Array’s prototype: ‘Array.prototype.forEach = function (...) {...}’. Your function does not, and therefore you cannot use member access on an array to get that value (the function), and that’s why your code is borked.
Fortunately for you, a variable ‘forEach2’ exists in your current scope, and you can just use it without needing to do any member access! You just write ‘forEach2(students);’ and that’s all.
But what if you want to access that function anywhere you have an array? You have two options: put it on every instance of your arrays, or put it on Array’s prototype. ‘Array.prototype.forEach2 = forEach2;’ however, if you do this you will need to change your function a bit. Right now it expects the array as its first argument (‘arr1’), but its redundant to write ‘students.forEach2(students)’ because when a function is invoked immediately following member access, the function will be provided with a special variable ‘this’ which will have the value of the object for which you are accessing its member. So, in this case, you would omit the ‘arr1’ argument and instead just use the the special ‘this’ variable which is magically in scope within your function.
Array.prototype.forEach2 = function ()
{
for (var i = 0; i < this.length; i++)
{
console.log(this[i]);
}
}
I hope this clarifies some things for you, and I hope it raises a bunch more questions.
P.S: Adding things to prototypes is both powerful and considered harmful unless you know what you’re doing and have a good reason to do it (like writing polyfills)... so do it at your own peril and use responsibly.
For your example to work, simply add the following statement after definition of forEach2:
// Here you're redefining the built-in forEach function with your
// custom function
students.forEach = forEach2;
// Now this must work
students.forEach(students);
This is an explanation why is that:
An array is a particular implementation of the Object. Object is an aggregate of the property:value pairs, such as
{'some_property': '1',
'some_function': function(),
'forEach': function() {<function logic>},
...
}
Your error 'Uncaught TypeError: students.forEach2 is not a function' tells, that there is no property forEach2 (which supposed to be a function) in between the Object's properties. So, there are two methods to correct this situation: - add a property (method function) to the object, or alter the existing property with similar functionality (not necessarily though).
[1] The first method assumes you add a new property function using Array.prototype.forEach2 = function() {...}.
[2] The second - you may redefine or alter any property in the Object by just assigning a new value to that property: Object.forEach = forEach2
Because the function forEach2 is not available either within the Array.prototype or is not a direct property of that array, so, you have two alternatives to use that custom forEach:
Use a decorator to add that function to a specific array
var students = ['foo', 'bar', 'baz'];
function forEach2() {
for (i = 0; i < this.length; i++) {
console.log(this[i]);
}
}
function decorate(arr) {
arr['forEach2'] = forEach2;
return arr;
}
decorate(students).forEach2()
Add that function to the Array.prototype
var students = ['foo', 'bar', 'baz'];
Array.prototype.forEach2 = function() {
for (i = 0; i < this.length; i++) {
console.log(this[i]);
}
}
students.forEach2();
Both alternatives use the context this to get the current array.
I have an array var test = [1, 2, 3]. When using for in method to go through the array something wrong happened. Please see the snippet here:
var test = [1, 2, 3]
for (var i in test) {
alert(i);
}
It will alert "0", "1" and "2" here in the snippet. But in my computer, it will alert "0", "1", "2" and an "equal". I think equal is a method of the array object itself.
This behavior didn't appear a week ago, thus my previous code which was written using for in is strongly affected.
Can anyone suggest what causes this strange behavior? And how may I avoid this behavior without changing my code?
This is because some library you're using (or maybe even something you wrote yourself) does this:
Array.prototype.equals = function() { ... }
This is the downside of the in operator - it enumerates the extra properties too. You can use the hasOwnProperty() method to check i and ignore the properties that are not set on the object. Or, for arrays, use this instead:
for (var i = 0; i < test.length; i++ )
Of course, this only works on continuous arrays that start at 0.
for (var i in test) enumerates all properties of the test array (remember an Array is an Object and can have arbitrary non-numeric properties too). This includes not only the indexes of the array, but also any enumerable properties that someone else might have added to the Array object. This is generally considered the wrong way to enumerate an array. This structure is made to enumerate all properties of an object, not only the array indexes of an array.
Instead, you can use any of these:
for (var i = 0; i < test.length; i++)
test.forEach(fn);
test.every(fn);
test.some(fn);
These will only enumerate actual array items, not other properties added to the array.
On sparse arrays (where not all items have been initialized), the for loop will visit every item (even the uninitialized items), whereas the others will skip the uninitialized items.
The fact that you are seeing the equal property show up in your enumeration means that someone has added an enumerable property to the Array object and thus it shows up with the for (var i in test) form of enumeration.
In ES6, you can also use the of iteration technique and this will be safe from the non-array element properties and will return the same items that .forEach() will return. Note, it also differs from in because it delivers the actual array values, not the array indexes:
var test = [4, 7, 13];
test.whatever = "foo";
for (var item of test) {
console.log(item); // 4, 7, 13
}
I don't know how to word this problem exactly but I found this extremely wired.
Basically I did this test in chrome's developer tool console.
for (var request in [0,1,2]) { console.log(request);}
0
1
2
compare
the last four lines are all outputs from the for loop.
during the for loop, request got the value compare.
I wonder if this is a bug in chrome.
for ... in ... iterates over the enumerable properties of an object, and is not intended for array indices. Array indices are also enumerable properties, but as you've discovered anything unsafely added to Array.prototype will be returned too.
To safely add a (non-enumerable) method to Array.prototype in ES5 browsers you can use Object.defineProperty, e.g.:
Object.defineProperty(Array.prototype, 'compare', {
value: function() {
...
}
});
This will stop for ... in from breaking, but it's still the wrong tool for the job when the variable of interest is an array.
You're best off using an indexed for loop.
For..in also enumerates over inherited properties etc.
var request = [0,1,2];
for (var i = 0; i < request.length; i++) {
console.log(request[i]);
}
The top answer to this question:
stackoverflow previous answer
puts it better than I could:
in your case, the global "object-prototype" as a compare function declared for it, e.g...
object.prototype.compare = function (a,b) {return a === b}
...and so, whenever you iterate an object (an array being one kind of object) you also iterate over the "compare" function of it's prototype... which is a "member" of it.
As others pointed out for .. in is not the best way to iterate thru array. If you insist on using it for some reason - use hasOwnProperty method to determine that property indeed belongs to the array:
var arr = [0,1,2];
for (var request in arr ) {
if (arr.hasOwnProperty(request)) console.log(request);
}
Rookie JS question here:
for( var p in ['nodeName', 'nodeType', 'tagName', 'localName'] ) {
console.log( p + '=' + all[i][p] + '\n' );
}
I expected to see something like
nodeName=DIV
Instead, I get
0=undefined
Am I forced to assign the array to a variable, so that I can index into it? Is there a way to use this syntax in the for-in and retrieve the string from the array?
Thanks!
Using for..in for an array is almost always wrong. It iterates over object properties, not over values -s so in your case it yields you 0, 1, 2 and 3. It gets even worse if you decide to extend Array.prototype with custom methods (which, unlike extending Object.prototype is not a big no-go). Their names will also be iterated over when using for..in.
The proper way to do what you want is this:
var foo = [...];
for(var i = 0; i < foo.length; i++) {
// use foo[i]
}
or this (in modern browsers or with the function being shim'd):
[...].forEach(function(value) {
// use value
});
I have a Javascript object that I'm trying to use as a "hashmap". The keys are always strings, so I don't think I need anything as sophisticated as what's described in this SO question. (I also don't expect the number of keys to go above about 10 so I'm not particularly concerned with lookups being O(n) vs. O(log n) etc.)
The only functionality I want that built-in Javascript objects don't seem to have, is a quick way to figure out the number of key/value pairs in the object, like what Java's Map.size returns. Of course, you could just do something like:
function getObjectSize(myObject) {
var count=0
for (var key in myObject)
count++
return count
}
but that seems kind of hacky and roundabout. Is there a "right way" to get the number of fields in the object?
There is an easier way spec'd in ECMAScript 5.
Object.keys(..) returns an array of all keys defined on the object. Length can be called on that. Try in Chrome:
Object.keys({a: 1, b: 2}).length; // 2
Note that all objects are basically key/value pairs in JavaScript, and they are also very extensible. You could extend the Object.prototype with a size method and get the count there. However, a much better solution is to create a HashMap type interface or use one of the many existing implementations out there, and define size on it. Here's one tiny implementation:
function HashMap() {}
HashMap.prototype.put = function(key, value) {
this[key] = value;
};
HashMap.prototype.get = function(key) {
if(typeof this[key] == 'undefined') {
throw new ReferenceError("key is undefined");
}
return this[key];
};
HashMap.prototype.size = function() {
var count = 0;
for(var prop in this) {
// hasOwnProperty check is important because
// we don't want to count properties on the prototype chain
// such as "get", "put", "size", or others.
if(this.hasOwnProperty(prop) {
count++;
}
}
return count;
};
Use as (example):
var map = new HashMap();
map.put(someKey, someValue);
map.size();
A correction: you need to check myObject.hasOwnProperty(key) in each iteration, because there're can be inherited attributes. For example, if you do this before loop Object.prototype.test = 'test', test will aslo be counted.
And talking about your question: you can just define a helper function, if speed doesn't matter. After all, we define helpers for trim function and other simple things. A lot of javascript is "kind of hacky and roundabout" :)
update
Failure example, as requested.
Object.prototype.test = 'test';
var x = {};
x['a'] = 1;
x['b'] = 2;
The count returned will be 3.
you could also just do myObject.length (in arrays)
nevermind, see this: JavaScript object size
That's all you can do. Clearly, JavaScript objects are not designed for this. And this will only give you the number of Enumerable properties. Try getObjectSize(Math).