JavaScript — The weird parts

A journey through some language oddities

David Luecke

--

In this post I’d like to recap some of the things we talked about at the January YYCJS meetup. It was all about the weird parts of JavaScript. You can find the video on Youtube, the slides at yycjs.com/the-weird-parts and the live-coding parts on JSBin.

Many thanks to @alexisabril for sanity checking and Assembly (the fantastic host of the meetup) for the image. Also, check out this blog post by Minko Gechev (with the same title) talking about some other weirdnesses.

https://www.youtube.com/watch?v=MihuqHhnFVo

Warm Up

To get into the mood we talked about objects and that object properties can either be accessed using the . (dot) or [] (square brackets) operator where the dot operator only accepts valid JavaScript variable names and the square brackets can take any string:

var person = {
name: 'david',
'&weird property': 'YYCJS'
}

var prop = 'name';

person.name // -> David
person['name'] // -> David
person["name"] // -> David
person[prop] // -> David

person['&weird property'] // -> YYCJS

// ERROR
person.&weird property

There is no real difference between using single or double quotes for strings except which character you’ll have to escape. As always it makes sense to be consistent and there seems to be a general preference towards single quotes (less keys to press on most keyboards).

Function arguments

In a function arguments is a special array-like variable that contains all parameters passed to it:

function sum() {
var sum = 0;
for(var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
sum(1, 2, 3, 4); // -> 10

Using arguments is often used to emulate function overloading when a function is called with a different number of parameters.

arguments is not a real array, it is only an array-like object with a length property and indices. If you want to use array methods like push or pop, you can convert it using jQuery.toArray() or like this:

var args = Array.prototype.slice.call(arguments, 0);

Truthy- and falsyness

In JavaScript, values other than actual booleans can evaluate as true (truthy) or false (falsy) in a logical expression. The following values will be treated as falsy:

false
null
undefined
0
'' // empty string
person.undefinedProperty

They can, for example, be evaluated in an if statement:

var person = { name: 'David' }if(person.name) {
// Do stuff if property exists and is not falsy
}

Note: This does not work for undeclared variable names. If you want to see if a variable exists (and is not undefined) use the typeof operator:

if(typeof myvar !== 'undefined') {}

The result of a logical expression is not always a real boolean. Using || (or) will return the first truthy or last value in the expression. This is often used for assigning default values to an object or for default parameters in a function:

person.meetup = person.meetup || defaults.meetup || 'YYCJS';function sum(first, second) {
first = first || 0;
second = second || 0;

return first + second;
}
sum() // -> 0 instead of NaN
sum(1) // -> 1 instead of NaN

Note: I recommend only reassigning function argument variables for setting defaults and use a local variable if it will change in any other way.

Equality

Just on the heels of truthy and falsyness is equality comparison. The basic == (equal) and != (not equal) operators only compare the value (without the type), for example:

1 == 1 // -> true
1 == ‘1' // -> true
1 == 2 // -> false
// Now things get weird
'' == false // -> true
[] == false // -> true, [] not falsy though
null == undefined // -> true

The === (triple equal) and !== (not triple equal?) operators compare value and type:

1 === 1 // -> true
1 === '1' // -> false
1 === parseInt('1') // -> true
[] === false // -> false
null === undefined // -> false

Objects are always compared by their reference so the expression will only be true if it is actually the same object:

var person = { name: 'David' };
var otherPerson = person;
person === { name: 'David' } // -> false, created new object
person === otherPerson // -> true, same reference

For consistency and to avoid very hard to find bugs always use === and !==. Sometimes this might mean more code to write (like converting strings if you expect a number) but it will make things a lot easier and more predictable.

Scope

JavaScript only knows function scope. This means that blocks or for and while loops do not introduce a new variable scope effectively making it the same as declaring every variable at the beginning of the function.

Functions within other functions can access variables from their parent scope (closure) but not the other way.

var x = 'outer';
function count() {
for(var i = 0; i < 10; i++) {
var x = 'testing';
var inner = function() {
var i = 42;
// -> x === 'testing'
// -> i === 42
}
inner();
// -> i === current count
}
}
// -> x === 'outer'

Sometimes you might see all variables being declared at the beginning of the function. This is mainly a style question and not necessary if variable names are kept descriptive and functions are kept small.

Closures

Lets look at the following jQuery code snippet that creates ten buttons, adds a click handler and appends them to the body.

for(var i = 0; i < 10; i++) {
var button = $('<button>Button #' + i + '</button>');
button.click(function() {
alert(i);
});
$('body').append(button);
}

Clicking any button however will not alert this buttons number as you would expect but the number 10 for every button instead.

The reason is again variable scope. The click handler function accesses the the same variable from its parent and since it always gets executed after the loop is done, it will have the same value everywhere.

The trick here is to introduce a new scope through a wrapper function and call it with the current count. It will then be copied into a new variable counter:

for(var i = 0; i < 10; i++) {
var button = $('<button>Button #' + i + '</button>');
var wrapper = function(counter) {
button.click(function() {
alert(counter);
});
}
wrapper(i);
$('body').append(button);
}

Only primitive values (string, boolean, number) will be copied. If you pass an object you will get the same object reference which is important to know if you intend to modify that object.

Global variables

In browser environment, variables declared without var will automatically become global, in Node they will be global to the module:

function test() {
var local = 42;
global = 'global';
}

test();

This is probably always unintentional. A good way to avoid accidental global variable leaks in Node and newer Browsers is to enable ECMAScript 5 strict mode with

'use strict';

at the beginning of a function or JavaScript file. A global variable leak will then throw a ReferenceError.

If you do need global variable, add and access them through the global object (window in the Browser, global in Node) explicitly:

function test() {
var local = 42;
window.global = 'global';
}

test();

What is this?

In any function, this is a reserved keyword that refers to the owner of the function. For example when calling a function defined on an object, it will be the object itself:

var person = {
name: 'David',
sayHi: function() {
return 'Hello ' + this.name;
}
}

person.sayHi(); // -> 'Hello David'

this is also used in event handler functions, where it refers to the element(s) that triggered the event:

document.getElementById('mybutton').addEventListener('click',   
function() {
this.innerHTML = 'Button clicked';
});

The trinity of this

Being able to change the owner of a function can lead to some confusion what this currently refers to but there are only three rules for what it can be:

  1. The object, when the function is called on the object (using the . or [] operator)
  2. The new object when a function is called with the new operator
  3. The owner has been set with call, apply or bind
var person = {
name: 'David',
sayHi: function() {
return 'Hello ' + this.name;
}
};

function Dog(name) {
this.name = name;
}

var otherPerson = { name: 'Eric' };

// 1) The object, when the function is called on an object
person.sayHi(); // -> 'Hello David'
person['sayHi'](); // -> 'Hello David'
// 2) The new object when a function is called with new
var goofy = new Dog('Goofy');
goofy.name // -> 'Goofy'

// 3) The owner has been set with call, apply or bind
person.sayHi.call(otherPerson); // -> 'Hello Eric'

var hi = person.sayHi.bind(otherPerson);
hi(); // Hello Eric

Otherwise this will be the global object or undefined in strict mode:

var ownerless = person.sayHi;
ownerless() // "Hello " (or window.name or ReferenceError)

this and callbacks

Callbacks are used for any kind of asynchronous operations. A common pitfall is to forget that in any new callback you will probably lose the original this. To keep the reference you will need to store it in a new variable so that it can be referenced (through its closure):

$('button').click(function() {
// Store the old this reference (the clicked button)
var self = this;
$.getJSON('someFile.json', function(data) {
// Set the button content
$(self).html(data.text);
});
});

Conclusion

This is a definitely incomplete list of some JavaScript weirdnesses and things you might trip over especially when just starting out with JavaScript. In a follow up post I want to talk about inheritance in JavaScript (which was also covered in the meetup).

--

--