Posts

Showing posts with the label 12quirks

JavaScript quirk 8: array-like objects

[This post is part of a series on JavaScript quirks.] Some objects in JavaScript look like arrays, but aren’t. They are called array-like . This blog post looks at what exactly that means and how to best work with those objects. Array-like objects An array-like object has: indexed access to elements and the property length that tells us how many elements the object has. does not have: array methods such as push , forEach and indexOf . Two examples of array-like objects is the result of the DOM method document.getElementsByClassName() (many DOM methods return array-like objects) and the special variable arguments [1] . You can determine the number of arguments via arguments.length And you can access a single argument, e.g. read the first argument: arguments[0] Array methods, however, have to be borrowed. You can do that, because most of those methods are generic . Generic methods A generic method does not require this to be an array, it only requires this to have length an...

JavaScript quirk 7: inadvertent sharing of variables via closures

[This post is part of a series on JavaScript quirks.] Closures are a powerful JavaScript feature: If a function leaves the place where it was created, it still has access to all variables that existed at that place. This blog post explains how closures work and why one has to be careful w.r.t. inadvertent sharing of variables. Closures Let’s start with an example of a closure: function incrementorFactory(start, step) { return function () { // (*) start += step; return start; } } This is how you use incrementorFactory : > var inc = incrementorFactory(20, 2); > inc() 22 > inc() 24 During all of its lifetime, the inner function (*) has access to the variables start and step of the outer function incrementorFactory . Thus, incrementorFactory returns not only the function, but somehow attaches the variables start and step . The data structure in which both variables are stored is called an environment . An en...

JavaScript quirk 6: the scope of variables

[This post is part of a series on JavaScript quirks.] In most programming languages, variables only exist within the block in which they have been declared. In JavaScript, they exist in the complete (innermost) surrounding function: function func(x) { console.log(tmp); // undefined if (x < 0) { var tmp = 100 - x; // (*) ... } } The cause of the above behavior is hoisting : Internally, the declaration at (*) is moved to the beginning of the function (the assignment stays where it is). That is, what a JavaScript engine actually executes looks like this: function func(x) { var tmp; console.log(tmp); // undefined if (x < 0) { tmp = 100 - x; ... } } But there is a trick to limit the scope of a variable to a block, it is called Immediately Invoked Function Expression (IIFE, pronounced “iffy”). Below, we use an IIFE to restrict the scope of tmp to the then-block of...

JavaScript quirk 5: parameter handling

[This post is part of a series on JavaScript quirks.] The basics of parameter handling in JavaScript are simple, advanced tasks require manual work. This blog post first looks at the basics and then covers advanced topics. The basics of parameter handling Two facts form the core of JavaScript parameter handling. Fact: you can always pass an arbitrary amount of parameters When calling a function, you can pass as many or as few actual parameters as you want, independently of how many formal parameters are mentioned in the function declaration. Parameters that are missing have the value undefined . Parameters that are too many are ignored. Let’s use the following function for a demonstration: function f(x, y) { console.log('x: '+x); console.log('y: '+y); } You can call this function with arbitrary many parameters: > f() x: undefined y: undefined > f('a') x: a y: undefined > f('a', 'b') ...

JavaScript quirk 4: unknown variable names create global variables

[This post is part of a series on JavaScript quirks.] Normally, JavaScript automatically creates a global variable if you use an unknown variable name: > function f() { foo = 123 } > f() > foo 123 Thankfully, you get a warning in ECMAScript 5 strict mode [1] : > function f() { 'use strict'; foo = 123 } > f() ReferenceError: foo is not defined Reference: JavaScript’s strict mode: a summary

JavaScript quirk 3: normal equality (==)

[This post is part of a series on JavaScript quirks.] Let’s start with a simple rule: the normal equality operators == and != are so problematic that you should always use strict equality ( === and !== ). Some people say that there are exceptions to this rule, I disagree [2] . Keeping this rule in mind, we can now take a look at what is strange about == without burdening our minds unnecessarily. The “normal” equality operator ( == ) has many quirks. While it is forgiving, it does not adhere to the typical rules of truthy and falsy (see quirk 1 ): > 0 == false // OK true > 1 == true // OK true > 2 == true // not OK false > '' == false // OK true > '1' == true // OK true > '2' == true // not OK false Apart from that, it lets you compare values that aren’t really comparable: > '' == 0 true > '\n 123 \t' == 123 true The last check is true because conv...

JavaScript quirk 2: two “non-values” – undefined and null

[This post is part of a series on JavaScript quirks.] Most programming languages have only one value for “no value” or “empty reference”. For example, that value is null in Java. JavaScript has two of those special values: undefined and null . They are basically the same (something that will change with ECMAScript 6, as will be explained in the last post of this series), but they are used slightly differently. undefined is assigned via the language itself. Variables that have not been initialized yet have this value: > var foo; > foo undefined Similarly, JavaScript assigns undefined to missing parameters: > function id(x) { return x } > id() undefined null is used by programmers to explicitly indicate that a value is missing. E.g. for JSON.stringify() : > console.log(JSON.stringify({ first: 'Jane' }, null, 4)) { "first": "Jane" } Check: does a variable have a value? If you want to know whether ...

JavaScript quirk 1: implicit conversion of values

[This post is part of a series on JavaScript quirks.] JavaScript is very tolerant when it comes to accepting values. For example, everywhere it expects a number, it does not reject values from other types, but tries to convert them: > '5' - '2' 3 > '5' * '2' 10 Automatic conversion to boolean is rarely problematic and often useful. It is covered here as a preparation for later – we’ll use it to work around quirks. Automatic conversion to string, however, can cause problems. Implicit conversion to boolean: “truthy” and “falsy” values Whenever JavaScript expects a boolean value (e.g. for the condition of an if statement), any value can be used. It will be interpreted as either true or false . The following values are interpreted as false : undefined , null Boolean: false Number: -0 , +0 , NaN String: '' All other values are considered true . Values interpreted as false are called falsy , values interpreted ...

12 JavaScript quirks

A core of JavaScript (the so-called “good parts”) is elegant, but that core is often obscured by quirks. This introduction is the first of a series of blog posts that looks at twelve common quirks and how to best deal with them: Implicit conversion of values Two “non-values” – undefined and null Normal equality ( == ) Unknown variable names create global variables Parameter handling The scope of variables Inadvertent sharing of variables via closures Array-like objects Subtyping constructors Reading and writing of properties this in real functions The for-in loop A concluding post will cover ECMAScript 6 [1] , which will eliminate most of the above quirks. The series will provide a good overview of JavaScript. It is a translation of a previous blog post in German. ECMAScript 5 will be used and a basic knowledge of JavaScript is required, but much will be explained. I will post one quirk per week. Reference: ECMAScript: ES.next vers...