Function Declarations and Expressions
Functions in JavaScript are first class objects. That means they can be
passed around like any other value. One common use of this feature is to pass
an anonymous function as a callback to another, possibly an asynchronous function.
The function Declaration
The above function gets hoisted before the execution of the
program starts; thus, it is available everywhere in the scope it was
defined, even if called before the actual definition in the source.
foo(); // Works because foo was created before this code runs
function foo() {} |
foo(); // Works because foo was created before this code runs
function foo() {}
The function Expression
This example assigns the unnamed and anonymous function to the variable foo.
foo; // 'undefined'
foo(); // this raises a TypeError
var foo = function() {}; |
foo; // 'undefined'
foo(); // this raises a TypeError
var foo = function() {};
Due to the fact that var is a declaration that hoists the variable name foo
before the actual execution of the code starts, foo is already defined when
the script gets executed.
But since assignments only happen at runtime, the value of foo will default
to undefined before the corresponding code is executed.
Named Function Expression
Another special case is the assignment of named functions.
var foo = function bar() {
bar(); // Works
}
bar(); // ReferenceError |
var foo = function bar() {
bar(); // Works
}
bar(); // ReferenceError
Here, bar is not available in the outer scope, since the function only gets
assigned to foo; however, inside of bar, it is available. This is due to
how name resolution in JavaScript works, the name of the
function is always made available in the local scope of the function itself.
How this Works
JavaScript has a different concept of what the special name this refers to
than most other programming languages. There are exactly five different
ways in which the value of this can be bound in the language.
The Global Scope
When using this in global scope, it will simply refer to the global object.
Calling a Function
Here, this will again refer to the global object.
Calling a Method
In this example, this will refer to test.
Calling a Constructor
A function call that is preceded by the new keyword acts as
a constructor. Inside the function, this will refer
to a newly created Object.
Explicit Setting of this
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // array will expand to the below
foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3 |
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // array will expand to the below
foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
When using the call or apply methods of Function.prototype, the value of
this inside the called function gets explicitly set to the first argument
of the corresponding function call.
As a result, in the above example the method case does not apply, and this
inside of foo will be set to bar.
Common Pitfalls
While most of these cases make sense, the first can be considered another
mis-design of the language because it never has any practical use.
Foo.method = function() {
function test() {
// this is set to the global object
}
test();
} |
Foo.method = function() {
function test() {
// this is set to the global object
}
test();
}
A common misconception is that this inside of test refers to Foo; while in
fact, it does not.
In order to gain access to Foo from within test, it is necessary to create a
local variable inside of method that refers to Foo.
Foo.method = function() {
var that = this;
function test() {
// Use that instead of this here
}
test();
} |
Foo.method = function() {
var that = this;
function test() {
// Use that instead of this here
}
test();
}
that is just a normal variable name, but it is commonly used for the reference to an
outer this. In combination with closures, it can also
be used to pass this values around.
Assigning Methods
Another thing that does not work in JavaScript is function aliasing, which is
assigning a method to a variable.
var test = someObject.methodTest;
test(); |
var test = someObject.methodTest;
test();
Due to the first case, test now acts like a plain function call; therefore,
this inside it will no longer refer to someObject.
While the late binding of this might seem like a bad idea at first, in
fact, it is what makes prototypal inheritance work.
function Foo() {}
Foo.prototype.method = function() {};
function Bar() {}
Bar.prototype = Foo.prototype;
new Bar().method(); |
function Foo() {}
Foo.prototype.method = function() {};
function Bar() {}
Bar.prototype = Foo.prototype;
new Bar().method();
When method gets called on an instance of Bar, this will now refer to that
very instance.
Closures and References
One of JavaScript's most powerful features is the availability of closures.
With closures, scopes always keep access to the outer scope, in which they
were defined. Since the only scoping that JavaScript has is
function scope, all functions, by default, act as closures.
Emulating private variables
function Counter(start) {
var count = start;
return {
increment: function() {
count++;
},
get: function() {
return count;
}
}
}
var foo = Counter(4);
foo.increment();
foo.get(); // 5 |
function Counter(start) {
var count = start;
return {
increment: function() {
count++;
},
get: function() {
return count;
}
}
}
var foo = Counter(4);
foo.increment();
foo.get(); // 5
Here, Counter returns two closures: the function increment as well as
the function get. Both of these functions keep a reference to the scope of
Counter and, therefore, always keep access to the count variable that was
defined in that scope.
Why Private Variables Work
Since it is not possible to reference or assign scopes in JavaScript, there is
no way of accessing the variable count from the outside. The only way to
interact with it is via the two closures.
var foo = new Counter(4);
foo.hack = function() {
count = 1337;
}; |
var foo = new Counter(4);
foo.hack = function() {
count = 1337;
};
The above code will not change the variable count in the scope of Counter,
since foo.hack was not defined in that scope. It will instead create - or
override - the global variable count.
Closures Inside Loops
One often made mistake is to use closures inside of loops, as if they were
copying the value of the loop's index variable.
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
} |
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
The above will not output the numbers 0 through 9, but will simply print
the number 10 ten times.
The anonymous function keeps a reference to i. At the time
console.log gets called, the for loop has already finished, and the value of
i as been set to 10.
In order to get the desired behavior, it is necessary to create a copy of
the value of i.
Avoiding the Reference Problem
In order to copy the value of the loop's index variable, it is best to use an
anonymous wrapper.
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
} |
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
The anonymous outer function gets called immediately with i as its first
argument and will receive a copy of the value of i as its parameter e.
The anonymous function that gets passed to setTimeout now has a reference to
e, whose value does not get changed by the loop.
There is another possible way of achieving this, which is to return a function
from the anonymous wrapper that will then have the same behavior as the code
above.
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
} |
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
The arguments Object
Every function scope in JavaScript can access the special variable arguments.
This variable holds a list of all the arguments that were passed to the function.
The arguments object is not an Array. While it has some of the
semantics of an array - namely the length property - it does not inherit from
Array.prototype and is in fact an Object.
Due to this, it is not possible to use standard array methods like push,
pop or slice on arguments. While iteration with a plain for loop works
just fine, it is necessary to convert it to a real Array in order to use the
standard Array methods on it.
Converting to an Array
The code below will return a new Array containing all the elements of the
arguments object.
Array.prototype.slice.call(arguments); |
Array.prototype.slice.call(arguments);
Because this conversion is slow, it is not recommended to use it in
performance-critical sections of code.
Passing Arguments
The following is the recommended way of passing arguments from one function to
another.
function foo() {
bar.apply(null, arguments);
}
function bar(a, b, c) {
// do stuff here
} |
function foo() {
bar.apply(null, arguments);
}
function bar(a, b, c) {
// do stuff here
}
Another trick is to use both call and apply together to create fast, unbound
wrappers.
function Foo() {}
Foo.prototype.method = function(a, b, c) {
console.log(this, a, b, c);
};
// Create an unbound version of "method"
// It takes the parameters: this, arg1, arg2...argN
Foo.method = function() {
// Result: Foo.prototype.method.call(this, arg1, arg2... argN)
Function.call.apply(Foo.prototype.method, arguments);
}; |
function Foo() {}
Foo.prototype.method = function(a, b, c) {
console.log(this, a, b, c);
};
// Create an unbound version of "method"
// It takes the parameters: this, arg1, arg2...argN
Foo.method = function() {
// Result: Foo.prototype.method.call(this, arg1, arg2... argN)
Function.call.apply(Foo.prototype.method, arguments);
};
Formal Parameters and Arguments Indices
The arguments object creates getter and setter functions for both its
properties, as well as the function's formal parameters.
As a result, changing the value of a formal parameter will also change the value
of the corresponding property on the arguments object, and the other way around.
function foo(a, b, c) {
arguments[0] = 2;
a; // 2
b = 4;
arguments[1]; // 4
var d = c;
d = 9;
c; // 3
}
foo(1, 2, 3); |
function foo(a, b, c) {
arguments[0] = 2;
a; // 2
b = 4;
arguments[1]; // 4
var d = c;
d = 9;
c; // 3
}
foo(1, 2, 3);
Performance Myths and Truths
The only time the arguments object is not created is where it is declared as
a name inside of a function or one of its formal parameters. It does not matter
whether it is used or not.
Both getters and setters are always created; thus, using it has nearly
no performance impact at all, especially not in real world code where there is
more than a simple access to the arguments object's properties.
However, there is one case which will drastically reduce the performance in
modern JavaScript engines. That case is the use of arguments.callee.
function foo() {
arguments.callee; // do something with this function object
arguments.callee.caller; // and the calling function object
}
function bigLoop() {
for(var i = 0; i < 100000; i++) {
foo(); // Would normally be inlined...
}
} |
function foo() {
arguments.callee; // do something with this function object
arguments.callee.caller; // and the calling function object
}
function bigLoop() {
for(var i = 0; i < 100000; i++) {
foo(); // Would normally be inlined...
}
}
In the above code, foo can no longer be a subject to inlining since it
needs to know about both itself and its caller. This not only defeats possible
performance gains that would arise from inlining, but it also breaks encapsulation
because the function may now be dependent on a specific calling context.
Making use of arguments.callee or any of its properties is highly discouraged.
Constructors
Constructors in JavaScript are yet again different from many other languages. Any
function call that is preceded by the new keyword acts as a constructor.
Inside the constructor - the called function - the value of this refers to a
newly created object. The prototype of this new
object is set to the prototype of the function object that was invoked as the
constructor.
If the function that was called has no explicit return statement, then it
implicitly returns the value of this - the new object.
function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo(); |
function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo();
The above calls Foo as constructor and sets the prototype of the newly
created object to Foo.prototype.
In case of an explicit return statement, the function returns the value
specified by that statement, but only if the return value is an Object.
function Bar() {
return 2;
}
new Bar(); // a new object
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // the returned object |
function Bar() {
return 2;
}
new Bar(); // a new object
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // the returned object
When the new keyword is omitted, the function will not return a new object.
function Foo() {
this.bla = 1; // gets set on the global object
}
Foo(); // undefined |
function Foo() {
this.bla = 1; // gets set on the global object
}
Foo(); // undefined
While the above example might still appear to work in some cases, due to the
workings of this in JavaScript, it will use the
global object as the value of this.
Factories
In order to be able to omit the new keyword, the constructor function has to
explicitly return a value.
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar(); |
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
Both calls to Bar return the same thing, a newly create object that
has a property called method, which is a
Closure.
It should also be noted that the call new Bar() does not affect the
prototype of the returned object. While the prototype will be set on the newly
created object, Bar never returns that new object.
In the above example, there is no functional difference between using and
not using the new keyword.
Creating New Objects via Factories
It is often recommended to not use new because forgetting its use may
lead to bugs.
In order to create a new object, one should rather use a factory and construct a
new object inside of that factory.
function Foo() {
var obj = {};
obj.value = 'blub';
var private = 2;
obj.someMethod = function(value) {
this.value = value;
}
obj.getPrivate = function() {
return private;
}
return obj;
} |
function Foo() {
var obj = {};
obj.value = 'blub';
var private = 2;
obj.someMethod = function(value) {
this.value = value;
}
obj.getPrivate = function() {
return private;
}
return obj;
}
While the above is robust against a missing new keyword and certainly makes
the use of private variables easier, it comes with some
downsides.
- It uses more memory since the created objects do not share the methods
on a prototype.
- In order to inherit, the factory needs to copy all the methods from another
object or put that object on the prototype of the new object.
- Dropping the prototype chain just because of a left out
new keyword
is contrary to the spirit of the language.
In Conclusion
While omitting the new keyword might lead to bugs, it is certainly not a
reason to drop the use of prototypes altogether. In the end it comes down to
which solution is better suited for the needs of the application. It is
especially important to choose a specific style of object creation and use it
consistently.
Scopes and Namespaces
Although JavaScript deals fine with the syntax of two matching curly
braces for blocks, it does not support block scope; hence, all that is left
in the language is function scope.
function test() { // a scope
for(var i = 0; i < 10; i++) { // not a scope
// count
}
console.log(i); // 10
} |
function test() { // a scope
for(var i = 0; i < 10; i++) { // not a scope
// count
}
console.log(i); // 10
}
There are also no distinct namespaces in JavaScript, which means that everything
gets defined in one globally shared namespace.
Each time a variable is referenced, JavaScript will traverse upwards through all
the scopes until it finds it. In the case that it reaches the global scope and
still has not found the requested name, it will raise a ReferenceError.
The Bane of Global Variables
// script A
foo = '42';
// script B
var foo = '42' |
// script A
foo = '42';
// script B
var foo = '42'
The above two scripts do not have the same effect. Script A defines a
variable called foo in the global scope, and script B defines a foo in the
current scope.
Again, that is not at all the same effect: not using var can have major
implications.
// global scope
var foo = 42;
function test() {
// local scope
foo = 21;
}
test();
foo; // 21 |
// global scope
var foo = 42;
function test() {
// local scope
foo = 21;
}
test();
foo; // 21
Leaving out the var statement inside the function test will override the
value of foo. While this might not seem like a big deal at first, having
thousands of lines of JavaScript and not using var will introduce horrible,
hard-to-track-down bugs.
// global scope
var items = [/* some list */];
for(var i = 0; i < 10; i++) {
subLoop();
}
function subLoop() {
// scope of subLoop
for(i = 0; i < 10; i++) { // missing var statement
// do amazing stuff!
}
} |
// global scope
var items = [/* some list */];
for(var i = 0; i < 10; i++) {
subLoop();
}
function subLoop() {
// scope of subLoop
for(i = 0; i < 10; i++) { // missing var statement
// do amazing stuff!
}
}
The outer loop will terminate after the first call to subLoop, since subLoop
overwrites the global value of i. Using a var for the second for loop would
have easily avoided this error. The var statement should never be left out
unless the desired effect is to affect the outer scope.
Local Variables
The only source for local variables in JavaScript are
function parameters and variables declared via the
var statement.
// global scope
var foo = 1;
var bar = 2;
var i = 2;
function test(i) {
// local scope of the function test
i = 5;
var foo = 3;
bar = 4;
}
test(10); |
// global scope
var foo = 1;
var bar = 2;
var i = 2;
function test(i) {
// local scope of the function test
i = 5;
var foo = 3;
bar = 4;
}
test(10);
While foo and i are local variables inside the scope of the function test,
the assignment of bar will override the global variable with the same name.
Hoisting
JavaScript hoists declarations. This means that both var statements and
function declarations will be moved to the top of their enclosing scope.
bar();
var bar = function() {};
var someValue = 42;
test();
function test(data) {
if (false) {
goo = 1;
} else {
var goo = 2;
}
for(var i = 0; i < 100; i++) {
var e = data[i];
}
} |
bar();
var bar = function() {};
var someValue = 42;
test();
function test(data) {
if (false) {
goo = 1;
} else {
var goo = 2;
}
for(var i = 0; i < 100; i++) {
var e = data[i];
}
}
The above code gets transformed before execution starts. JavaScript moves
the var statements, as well as function declarations, to the top of the
nearest surrounding scope.
// var statements got moved here
var bar, someValue; // default to 'undefined'
// the function declaration got moved up too
function test(data) {
var goo, i, e; // missing block scope moves these here
if (false) {
goo = 1;
} else {
goo = 2;
}
for(i = 0; i < 100; i++) {
e = data[i];
}
}
bar(); // fails with a TypeError since bar is still 'undefined'
someValue = 42; // assignments are not affected by hoisting
bar = function() {};
test(); |
// var statements got moved here
var bar, someValue; // default to 'undefined'
// the function declaration got moved up too
function test(data) {
var goo, i, e; // missing block scope moves these here
if (false) {
goo = 1;
} else {
goo = 2;
}
for(i = 0; i < 100; i++) {
e = data[i];
}
}
bar(); // fails with a TypeError since bar is still 'undefined'
someValue = 42; // assignments are not affected by hoisting
bar = function() {};
test();
Missing block scoping will not only move var statements out of loops and
their bodies, it will also make the results of certain if constructs
non-intuitive.
In the original code, although the if statement seemed to modify the global
variable goo, it actually modifies the local variable - after hoisting
has been applied.
Without knowledge of hoisting, one might suspect the code below would raise a
ReferenceError.
// check whether SomeImportantThing has been initialized
if (!SomeImportantThing) {
var SomeImportantThing = {};
} |
// check whether SomeImportantThing has been initialized
if (!SomeImportantThing) {
var SomeImportantThing = {};
}
But of course, this works due to the fact that the var statement is being
moved to the top of the global scope.
var SomeImportantThing;
// other code might initialize SomeImportantThing here, or not
// make sure it's there
if (!SomeImportantThing) {
SomeImportantThing = {};
} |
var SomeImportantThing;
// other code might initialize SomeImportantThing here, or not
// make sure it's there
if (!SomeImportantThing) {
SomeImportantThing = {};
}
Name Resolution Order
All scopes in JavaScript, including the global scope, have the special name
this, defined in them, which refers to the current object.
Function scopes also have the name arguments, defined in
them, which contains the arguments that were passed to the function.
For example, when trying to access a variable named foo inside the scope of a
function, JavaScript will look up the name in the following order:
- In case there is a
var foo statement in the current scope, use that.
- If one of the function parameters is named
foo, use that.
- If the function itself is called
foo, use that.
- Go to the next outer scope, and start with #1 again.
Namespaces
A common problem associated with having only one global namespace is the
likelihood of running into problems where variable names clash. In JavaScript,
this problem can easily be avoided with the help of anonymous wrappers.
(function() {
// a self contained "namespace"
window.foo = function() {
// an exposed closure
};
})(); // execute the function immediately |
(function() {
// a self contained "namespace"
window.foo = function() {
// an exposed closure
};
})(); // execute the function immediately
Unnamed functions are considered expressions; so in order to
being callable, they must first be evaluated.
( // evaluate the function inside the parentheses
function() {}
) // and return the function object
() // call the result of the evaluation |
( // evaluate the function inside the parentheses
function() {}
) // and return the function object
() // call the result of the evaluation
There are other ways to evaluate and directly call the function expression
which, while different in syntax, behave the same way.
// A few other styles for directly invoking the
!function(){}()
+function(){}()
(function(){}());
// and so on... |
// A few other styles for directly invoking the
!function(){}()
+function(){}()
(function(){}());
// and so on...
In Conclusion
It is recommended to always use an anonymous wrapper to encapsulate code in
its own namespace. This does not only protect code against name clashes, but it
also allows for better modularization of programs.
Additionally, the use of global variables is considered bad practice. Any
use of them indicates badly written code that is prone to errors and hard to maintain.