"Use the DevTools Console, Luke!"
"==" is not "=". if (a = 'one') assigns 'one' to a and is True. if (a == 'one') compares a to 'one' returning true or false. To avoid this mistake, always put the constant first in a comparison. e.g. if ('one' = a) will throw an error, catching your mistake. Also, there is a "===" which checks if two labels reference the same object.
Mismatched parentheses. It's easy to forget a closing ) or } or to get them in the wrong order. And it can be VERY difficult to find once you have messed it up. Use the highlighting feature: Click on the opening or closing character, and look for the highlight of the matching one. If it isn't where you expect, check inside that block.
0 index vs 1 index. Arrays in Javascript are indexed from ZERO not ONE. The "first" item in an array is at array[0]
Typos: Things that look like each other. . vs , and : vs ;. rn vs m. Lint helps. Careful slow reading is also good.
Bad documentation is a bug. If the documentation says it works one way, and it works another way, that's a problem. Some people claim that "self documenting" code is best, but I've always found that a few notes in English to future me are (later) very appreciated. However, here are a few tricks for writing code that is clear:
true == 1 and false == 0 so true - true == false. True and false are just numbers. Anything that comes out 0 is false, and anything that isn't zero is true. Inside a computer, /everything/ is a number, even strings, or objects, or programs. Everything. The job of a language developer is to turn human ideas into numbers with the least surprise. Let's see how the developers of Javascript did:
9 + "1" returns 91 but 9 - "1" returns 8. This happens because the "+" operator has two different meanings to humans: Add and Concatenate. Add is just good old math adding 2 numbers. Concatenate is tacking one thing after another; like "9" + "1" is "91". So if "+" gets handed a number and a string, should it add or concatenate? Well... every number can be converted to a string, but not every string can be converted to a number, so... 9 gets converted to "9" and the "1" gets tacked on. But why the 8 as a result in the second case? Well, minus only knows how to subtract or negate numbers. So it tries to convert "1" to a number, and when that works, it continues and returns 8. Now, -'s other function is negate, so if you like you can do 9- -"1" and get 10. But not 9 --"1" because -- is decrement. By the way, 9 - "A" or anything else that can't be converted to a number returns NaN ("Not A Number"). Best just use parseInt() or parseFloat(). e.g. 9 + parseInt("1") will always get you 10. And 0.1 + parseFloat("0.2") ... See the next item:
(0.1+0.5==0.6) will return true but (0.1+0.2==0.3) will return false. This actually makes perfect sense once you understand that computers do math in binary and humans read numbers in decimal. Converting from one to the other introduces small errors. And some values cause more errors than others. 0.2 is a really difficult value. 0.1+0.2 ends up becoming 0.30000000000000004 which is why it doesn't equal 0.3. This video does more to explain why:
https://www.youtube.com/watch?v=PZRI1IfStY0
To get around this, you must round your answers. Math.round((0.1+0.2)*1000)/1000 gives you 0.3
[]+[] returns "". So 2 empty arrays become an empty string. This is because the "+" operator only knows how to work on two things: Numbers and strings. So when you hand it something else, it tries to convert it to a string. An empty array gets converted to an empty string, and two of them concatenated together are still empty.
('b' + 'a' + + 'a' + 'a').toLowerCase() Can you figure out what that evaluates to? Try it in the debug console. (Press Ctrl + Shift + I, and enter it into the console, press enter)
Value vs Reference: Variables have values. Some types of variables have a value which is a reference to another thing, which might have a name or not. For example: x = 10 sets the value of x to 10. No worries. y = x sets y to the value x currently holds, so y is also 10. If we say y = 20 then y is now 20, but x is still 10. No surprises.
Lets try again with something less "primitive": x = {value: 10} sets the value of x to an address which references a new object, {} makes block objects, containing a label "value" which has the value 10. x.value is 10, but x "is" {value:10} or at least points to the thing that is. Now, y = x will show y.value == 10 and y points to this {value:10} thing. But if we do y.value = 20 and then look at x.value, we will find it has now become 20. Why? Because "thing" was only made once. Both x and y only pointed to it. There was only ever one .value and we changed it's value via y. We did NOT change y.
If we want a completely independent y from our x, we need to duplicate, or
"clone" the object. And that, is a bloody nightmare:
https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object
But for general use in most cases, y = JSON.parse(JSON.stringify(x))
is a good bet. JSON is worth learning.
String.indexOf(substring) does not bool well: The indexOf method for a string object returns an integer value, starting from zero, indicating the beginning of the substring within the String object. If the substring is not found, a -1 is returned. If the substring matches the start of the string, 0 is returned, and so on.
You might try to use this to test if a substring is present in a string, but note that -1 is NOT false (only 0 is) so text.indexOf("t") is false if the first character in the string is "t" but true if the second (or following) character is "t" and it is true if "t" is not found in the string.
if ("test".indexOf("t")) {console.log("true")} undefined if ("test".indexOf("e")) {console.log("true")} true if ("test".indexOf("z")) {console.log("true")} true
Turns out when used with a number, the Tilde operator (bitwise not) effective does ~N => -(N+1). This expression evaluates to 0 only when N == -1. We can leverage this by putting ~ in front of the indexOf(...) function to do a boolean check if an item exists in a String or an Array.
if (~strvar.indexOf(substring)) { //substring was found
if (!~strvar.indexOf(substring)) { //substring was NOT found
Math.max() returns -Infinity and Math.min() returns Infinity. Seems a bit backwards huh? So first, you have to realize this is not how these were supposed to be used; something like Math.max(1,2,3) is more reasonable and does, in fact, return 3. But how do you write a program to find the maximum of a set of numbers? At some point, you need to have a line like if (current_max < next_value) current_max = next_value; right? And you call that for each value you get and the biggest one shakes out. But what value do you start current_max off with? If you set it to 10, then called your function with 1, 2, 3 what would happen? So how about starting at zero? Well, Math.max(-1,-2,-3) will give you zero instead of -3 then. So you need to start with a REALLY small number... like... negative infinity. And if you don't pass it any numbers, it just returns that starting value. A longer version
Math.Infinity == Math.Infinity returns false You might think that would be true, but because "Math" it's not. Because == is basically subtraction, then comparison with zero. e.g. x == y is tested by doing x - y == 0. Infinity is a special sort of non-number and only numbers can be subtracted. So you can't compare infinity with infinity because you can't subtract them. 1
Comment out sections. Never use /* */ in code so the */ doesn't un-comment previously commented-out parts. Use // in code and /* */ to debug by removing parts to focus on problem areas.
Also: A Short tutorial, taken from Debugging Jobs in DDE (30 min)