Gotcha answersΒΆ

This page contains the answers to the Gotcha quiz.

Equality

if (a == b) {
   ...
}

Ordinary equality (==) can be surprising and so use === instead:

0 == '0'                     // True
0 == ''                      // True
'' == '0'                    // False.  Not transitive!
undefined == null            // True

I don’t know of any situations where == is preferred. To compare two values after conversion to string (respectively number) make it explicit by writing respectively:

'' + x === '' + y
 +x === +y

Question.

Addition

x = (a + b) + c
y = a + (b + c)
x == y                          // True or False?  When?

In string + number the number is silently converted to a string.

js> typeof ('1' + 2)
string

js> ('1' + 2) + 3
123
js> '1' + (2 + 3)
15

In number + string the number is again silently converted to a string.

js> typeof (1 + '2')
string

js> (1 + 2) + '3'
33
js> 1 + (2 + '3')
123

If conversion to a string is required make it explicit by writing:

'' + a + b + c

Question.

Trailing comma

x = [
   'first value',
   'second value',
   'third value',
   ]

Doing this is good in Python and bad in JavaScript. In Python it makes it easier to reorder, insert and delete values. In JavaScript you’ll get, depending on the browser a syntax error (IE) or a trailing undefined in the array (FF).

Question.

Missing comma

x = [
  [1, 2, 3],
  [4, 5, 6]
  [7, 8, 9]
]

In Python you’d get an TypeError from this code, as in:

py> [4, 5, 6][7, 8, 9]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not tuple

In JavaScript, we have:

js> [4, 5, 6][7, 8, 9] === undefined
true

and so the assignment to x is equivalent to

x = [ [1, 2, 3], undefined ]

Question.

Missing new

var Point = function(x, y){
    this.x = x;
    this.y = y;
}
pt = Point(2, 4)

After running this code we have, at the command line:

js> x
2
js> y
4

In other words, we’ve assigned values to the global object! In addition, we have:

js> pt == undefined
true

Question.

Bind is transient

fn = obj.method;                 // General form of the gotcha.

x = [0, 1, 2, 3, 4, 5];          // Example of gotcha.
a = x.slice(1, 3);               // What happens behind the scenes?
tmp = x.slice;                   // What happens here?
b = tmp(1, 3);                   // What happens here?

Python uses bound methods and so in Python a.b is a first class object.

class A(object):

    def b(self):
        pass

py> a = A()                      # Create an instance.
py> a.b
<bound method A.b of <__main__.A object at 0x21d7910>>

JavaScript uses what can be called transient bound methods. Thus

js> a = [1, 2, 3]
1,2,3
js> b = [4, 5, 6, 7, 8]
4,5,6,7,8
js> a.slice === Array.prototype.slice
true
js> a.slice === b.slice      // No implied reference to *this*.
true
js> a.slice(1, 3)            // Transient implied ref to *this*.
2,3
js> b.slice(1, 2)            // Difference transient implied ref.
5,6

The function call

b = tmp(1, 3);

is equivalent to calling Array’s slice method on the global object, which is probably not what is wanted!

Question.

Missing var

var average = function(a, b){
    total = a + b;
    return total / 2;
}

The Python code

def average(a, b):
    total = a + b
    return total / 2.0

works because in Python total is recognised as being local to the function average.

In JavaScript variables are global unless explicitly declared as local.

The sample code has a interesting side effect. It assigns to the global variable total.

js> average(3, 7)
5
js> total
10

Question.

Missing closure

// Add handlers: click on item to show item number.
var make_and_add_handlers(items){

    for(var i=0; i < items.length; i++){

       items[i].onclick = function(){
           alert("This is item " + i);
       };
    }
}

This code, when run, gives all items the same number (namely number of items less one). This is because only one value of i is captured. (It is not because all items are getting the same onclick function. They are getting different functions, but with identical behaviour.)

Here’s one solution. In the main function instead write

items[i].onclick = alert_n(i);

where we have:

var alert_n(n){
    return function(){
        alert("This is item " + n);
    };
};

When we do this, each onclick function captures its own copy of the argument i to alert_n. (Closure capture function parameters in the same way as it captures function variables.)

Question.

Missing that

// Return fn that does something (and logs the instance).
proto.fn_factory = function(...){

   var fn = function(...){
       ...
       log("Name = " + this.name);
       ...
   }
   return fn;
}

// Example of desired output.
js> instance.name
Charles
js> fn = instance.fn_factory(...)
js> fn(...)                     // Logging to go to console.
Name = Charles

The code above (provided the global object does not have a name attribute) produces:

js> fn(...)                     // Logging to go to console.
Name = undefined

When a JavaScript function executes, what this refers to depends on the calling context. In particular, in

var fn2 = function(...){

    this.attribute = '123';

    return function() {
       return this.attribute;
    }

the two occurences of this usually refer to completely different objects. To fix instead write:

var fn2 = function(...){

    this.attribute = '123';

    var that = this;
    return function() {
       return that.attribute;
    }

This works because that does not have the special role this has. In the returned function, that refers to the same object as that does in main part of fn2, which in turn refers to the same object as the this object in the body.

It is customary to write that=that to achieve this effect. (Following the custom means no further explanation is required.)

Question.