I spent hours just to find out that I misspelt the word .length as .lenght. It can run normally with no warning at all. Why...?
I use 'use strict' and run on Node.js 10.13.0.
Code:
'use strict';
let arr = [1, 2, 3, 4];
for(let i = 0; i < arr.lenght; i++) {
console.log(arr[i]);
}
Because when you try to get a property that doesn't exist, it returns undefined, and 0 < undefined is false.
let arr = [1, 2, 3, 4];
console.log(arr.lenght) // undefined
console.log(arr.qwerty) // undefined
console.log(arr.lenght < 9999) // false
console.log(arr.lenght > 9999) // false
arr.length = 7 // <-- it's not a good idea
for(let i = 0; i < arr.length; i++) {console.log(arr[i])}
EDIT
I said 'javascript is not a strongly typed language' and it is true. But this way of adding new properties it is a feature of prototype-based programming, as #Voo said.
I also said .length=7 it's a bad idea. After reading a little more, in this case I still think it's a little weird to increase the length property after adding elements. Maybe it's fine to truncate, delete elements or empty an array, although in the latter case I would prefer arr=[] instead of arr.length=0.
There are some interesting examples about length property in the Mozilla documentation.
A JavaScript array's length property and numerical properties are
connected. Several of the built-in array methods (e.g., join(),
slice(), indexOf(), etc.) take into account the value of an array's
length property when they're called. Other methods (e.g., push(),
splice(), etc.) also result in updates to an array's length property.
var fruits = [];
fruits.push('banana', 'apple', 'peach');
console.log(fruits.length); // 3
When setting a property on a JavaScript array when the property is a
valid array index and that index is outside the current bounds of the
array, the engine will update the array's length property accordingly:
fruits[5] = 'mango';
console.log(fruits[5]); // 'mango'
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 6
Increasing the length.
fruits.length = 10;
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 10
Decreasing the length property does, however, delete elements.
fruits.length = 2;
console.log(Object.keys(fruits)); // ['0', '1']
console.log(fruits.length); // 2
JavaScript arrays are treated as objects (though they are instances of Array). Hence, when you write arr.lenght, it treats lenght as a property of an object that is undefined. Hence, you don't get an error.
It simply tries to get a property that is undefined. Also, in your case, the loop just does not execute as the condition of the loop is never satisfied.
Why
Standard JavaScript arrays aren't really arrays at all¹, they're objects, and if you read an object property that doesn't exist (like lenght), you get the value undefined (even in strict mode):
console.log(({}).foo); // undefined
When you use undefined in a relational operation like < or > with a number, it gets converted to a number, but the number value it gets is the special number NaN, which has the bizarre property of always causing comparisons to be false:
console.log(NaN < 0); // false
console.log(NaN > 0); // false
console.log(NaN === 0); // false
console.log(NaN === NaN); // false!!
What you can do about it
Linter tools will often pick these things up in simple cases.
Alternately, TypeScript provides a full static typing layer on top of JavaScript which can catch these sorts of errors.
If you wanted (and this would probably be overkill), you could wrap a Proxy around your objects that threw a proactive error when you tried to read a property that didn't exist:
function proactive(obj) {
return new Proxy(obj, {
get(target, propName, receiver) {
if (!Reflect.has(target, propName)) {
throw new TypeError(`Property '${propName}' not found on object`);
}
return Reflect.get(target, propName, receiver);
}
});
}
const a = proactive(["a", "b"]);
a.push("c");
for (let i = 0; i < a.length; ++i) {
console.log(a[i]);
}
console.log(`Length is: ${a.lenght}`); // Note the typo
.as-console-wrapper {
max-height: 100% !important;
}
There's a significant runtime penalty, though.
¹ (that's a post on my anemic little blog)
You could easily add new properties to arr object, JavaScript won't warn you about it, instead it will try to find the property you're calling, and if it didn't find anything such result will be undefined, so the comparison is actually i < undefined everytime because you're calling a property that hasn't been created on the object. I'll suggest you to read What does "use strict" do in JavaScript, and what is the reasoning behind it?.
The upper bound of the loop is specified as lenght, a typo for the local variable length. At runtime, lenght will evaluate to undefined, so the check 0 < undefined is false. Therefore the loop body is never executed.
By default, all objects in JavaScript are extensible, which means that you can add additional properties to them at any time simply by assigning a value to them.
Arrays are no different; they're simply objects that are instances of the Array type (at least for the purposes of extensibility).
In this case, had you added:
Object.preventExtensions(arr);
after creating the array, then in combination with 'use strict' this would have raised an error -- had you tried to write to a typo'd property. But for a read usage like this, there is still no error at all; you just get undefined.
This is just one of the things you have to live with in a loosely-typed language; with the added flexibility comes added risk of bugs if you're not careful.
I have the following code:
j = 1;
while (j < 50) {
//requirement:
console.log(JSONitem [j]["criteria"]);
reqtest = Object.keys(JSONitem [j]["criteria"]);
....
j++;
}
But when I execute this, I get the following error:
TypeError: JSONitem[j] is undefined
The output object from the console.log part is right, but also in this line is the TypeErro above. I think, the "j" does not replace the number, but in the output console it works...
Thanks!
console.log(...JSONitem.map(el=>el.criteria));
Your code may behaves unexpected if the length of your array (?) changes. You should use the Array.prototype methods or the for..in, for..of or
for(i=0;i<array.length;i++)
loops to iterate over your Array.
I use promises in angular to get some data from the server. In promises success I do
promise.success(function (data, status) {
for (i = 0; i <= data.data.length; i++){
$scope.anArray[i] = data.data[i][1] ;
}
}
I do this because data that came from the server have this structure
{"result":"answered","data":[["jake","508"],["amir","602"],["ben","450"]]}
and I ONLY want to get the numbers 508 , 602 and 450. But I always get the same error TypeError: Cannot read property '0' of undefined reffering to this line : $scope.anArray[i] = data.data[i][0] ;.
Those numbers are feeded to a library to create a chart. Sometimes the chart is created, some times is not. Even if it is created, the same error is always there.
I dont know how to fix that error and getting the chart to be always created without any problems. Looks like the code doesnt like the data.data[i][0] structure, having a simple array like data.data[i] doesnt create an error.
What should I do?
Thanks
Your for loop has an extra execution. You've initialized i to 0 but you've also used <= which means you are going to execute on array[array.length] which is out of bonds. That returns undefined and when you try to access the [0] index on that undefined you get that error.
Change to
for (i = 0; i < data.data.length; i++){
If you're able to do assignment personally I would go with
$scope.anArray = data.data.map(entry => entry[1]);
and skip the for loop entirely. Note if you want to avoid lambdas this would be
$scope.anArray = data.data.map(function(entry) { return entry[1]; });
How to loop through this data: (I have no control over format)
{"rowCount":3,"1":{"K":"2009","V":"Some Data"},"2":{"K":"2010","V":"More Data"}}
Above is a console.log(results) and results is a string
var r = JSON.parse(results);
var t;
for(var i=1;i<=r.rowCount;i++) {
t=r[i].V;
tableData.push(
{title:t, year:'2009', hasChild:true, color: '#000'}
);
}
Error: TypeError: 'undefined' is not an object (evaluating 'r[i].V')
I cannot get it to evaluate the variable i. What am I doing wrong?
Thanks
UPDATE
The incoming data had a bad rowcount causing the error. The accepted answer however is correct... just user error on my part not catching the bad incoming data. Had I put a console.log inside the loop I would have realized the error was actually happening after two successful loops. oops
I assume r.rowCount should be j.rowCount.
Ideally you should also initialise the i variable if you haven't already (i.e. with the var keyword).
(I've also moved the var t declaration outside the loop, to make it clear that it's the same t throughout and you're just changing its value. You shouldn't redeclare it with var each time – although I doubt this affects the output.)
var j = {"rowCount":2,"1":{"K":"name","V":"john"},"2":{"K":"name","V":"sue"}};
var t;
for (var i = 1; i <= j.rowCount; i++) {
t = j[i].V;
console.log(t);
}
Working demo – JSFiddle
here is the function:
var M = [];
function haveComponents () {
var a = 0;
for (var n in this.M) a++;
return a > 0;
}
I would like to understand:
the construct of "for(var n in this.M)"; I'm used to a regular for loop and I'm not familiar with this construct.
how "this.M" fits into the code i.e. its purpose
generally speaking, what this function would likely be used for.
Thanks
There appears to be some missing code.
var M = [];
Assigns a new array to the variable M, which seems to be a global variable (but likely isn't, you just haven't shown enough code to properly determine the context).
haveComponents: function () {
That appears to be part of an object literal that assigns a function to a property called haveComponents.
var a = 0;
Creates a local variable a and when the code executes, assigns it a value of 0.
for (var n in this.M) a++;
Creates a local variable n and sequentially assigns it the name of an enumerable property of whatever this.M references. If this is the global object, M will be the array initialised above. If not, it may or may not be something else. You haven't shown any other assignment, or what this has been set to.
For each enumerable property of M (which includes its inherited properties), a will be incremented by one.
return a > 0;
}
Returns true if a is greater than zero.
An equivalent function is:
haveComponents: function () {
for (var n in this.M) {
// this.M has at least one enumerable property
return true;
}
// this.M has no enumerable properties
return false;
}
or for the purists:
haveComponents: function () {
var hasEnumerable = false;
for (var n in this.M) {
hasEnumerable = true;
break;
}
return hasEnumerable;
}
The function counts how many elements are in the M array.
The for in allows you to iterate object's enumerable properties , note that this is different from a for each behaviour where the iteration is over items rather tha properties. In javascript this translates into going into the prototype property names and list them as well, possibly resulting in unexpected result.
for(var n in this.M) this is a for-each loop, used to iterate over a set of values instead that by using conditions. It is used to iterate over properties of objects.
the this keyword refer to the owner of the function (whose is the haveComponents function), while M is a property of this
this function just, uselessly, counts elements in M to see if they are more than 0. Counting them is absolutely superfluous for this purpose though.
The for(var n in this.M) iterates through all of the elements of this.M, successively storing them in the variable n.
I have no idea what this.M is, that depends on where your code comes from.
In general, I would say that this code returns whether M is empty or not (and returns true if it is not empty).