Things to be aware when you are starting in JavaScript

JavaScript is the world’s most misunderstood programming language, said Douglas Crockford. I tried to compile some topics which I think is too confusing to work with if you are just starting in JavaScript. It is almost about the scope.

Table of contents

  1. Scope
  2. What is Scope?
  3. Global Scope
  4. Local Scope
  5. Function Scope
  6. Lexical Scope
  7. The this keyword
  8. Closure
  9. Hoisting

1. Scope

Different from other languages like C, C++ or C#, JavaScript doesn’t has block-level scope, but it has function-level scope. Which means that only function can create a new scope. They aren’t created by for or while loops or expression statements like if ir switch.

1.1. What is Scope?

Be aware that the scope refers to the context of your code so it could be globally or locally defined. Understanding it is key to be a better JavaScript developer by knowing where variables and function are accessible and be able to change the scope of your code’s context when it is neccessary.

1.2. Global Scope

By default, when write a line of JavaScript you’re in what is called Global Scope or Global Object. So, if we are declaring a variable in the default scope, we are doing it on global scope.

// app.js
var fullName = "David Gilmour";

Control the global scope is easy and in doing so, you’ll run into no issues with global scope. You’ll hear people saying “Global Scope is bad”, but it isn’t. You need it to creade Modules and APIs that are accessible across scopes and take it as your advantage.

Think about it as a global object where each global variable is present as a property of this object. In browsers, the global scope object is stored in the window variable.

1.3. Local Scope

Everything defined past the global scope refers to local scope. You’ll have one global scope and for each function defined it has its own local scope. Like I said before, only functions can create new scopes. So, if I have a function with variables inside it, those variables are in the local scope (the function scope). Look at the example below.

// Scope A: Global scope
var myFunction = function() {
	// Scope B: Local scope
	var fullName = "David Gilmour";
	console.log(fullName); // David Gilmour
}
console.log(fullName); // Uncaught ReferenceError: fullname is not defined

We can’t access the fullName variable because it is locally scoped and it isn’t exposed to the parent scope.

1.4. Function Scope

Just remember: Functions creates scopes.

// Scope A
var myFunction = function() {
	// Scope B
	var someOtherFunction = function() {
		// Scope C
	}
}

1.5. Lexical Scope

Also called as Closure or Static Scope, Lexical Scope is the possibility of an inner scope access a variable or function in parent scope.

// Scope A
var myFunction = function() {
	// Scope B
	var fullName = "David Gilmour"; // It is defined in Scope B
	var someOtherFunction = function() {
		// Scope C
		console.log("I'm listening " + fullName + " albuns.");
	}
	console.log(fullName); // "David Gilmour"	
	someOtherFunction(); // Will log out: "I'm listening David Gilmour albuns."
}

Lexical scope is easy to understand and work with. Just think that any variable, object and functions defined in it’s parent scope are available in the scope chain. The important thing to remember is that Lexical scope doesn’t work backwards.

// Scope A
// fullName = undefined
var myFunction = function() {
	// Scope B
	// fullName = undefined
	var someOtherFunction = function() {
		// Scope C
		var fullName = "David Gilmour"; // Local scope
	}
}

##2. The this keyword this keyword could be hard to use if you don’t exacly know how it works. The this keyword always refers to owner of the function we’re executing, or rather, to the object that a function is a method of.

Here is a few very important points to remember about the this keyword:

  • The this keyword value isn’t related to the function itself but how the function is called. So it can be dynamic.
  • Yoo can change the this context through .call(), .apply() and .bind()

Default context

We can change the this vaue in a few different ways and it’s usually the caller of a function that can change his context.

Window Object, Global Scope

From a regular function declaration we can expect that the this value would be window Object, which refers to the global scope. The ownership of this will be window.

var someFunction = function() {
	console.log(this); // [object Window]	
};

Object literals

In the case of Object literals, the ownership will be the it’s own Object.

var someObject = {};
someObject.someMethod = function() {
	console.log(this);	// someObject
};

So, why isn’t window the ownership in this case? Because the window Object didn’t call the function but someObject did. It is the same for constructors.

Prototypes and Constructors

var musician = function() {
	var _firstName;
	var _lastName;
	this.setFirstName = function(firstName) {
		this._firstName = firstName;
	};
	this.setLastName = function(lastName) {
		this._lastName = lastName;
	};
};

musician.prototype.getFullName = function() {
	return this._firstName + ' ' + this._lastName;
};


var Gilmour = new musician();
Gilmour.setFirstName('David');
Gilmour.setLastName('Gilmour');
Gilmour.getFullName(); // David Gilmour

In both cases above, the this ownership is the Constructor object, which is musician;

Events

Same rules are applied when we bind events. The this refers to his owner.

// .musician = <div class="musician">David Gilmour</div>
var musician = document.querySelector('.musician');
var someFunction = function() {
	console.log(this); // <div class="musician">David Gilmour</div>
};
element.addEventListener('click', someFunction, false);

To see how this is dynamic you can just invoke the function and the return will be different from the event call.

// .musician = <div class="musician">David Gilmour</div>
var musician = document.querySelector('.musician');
var someFunction = function() {
	console.log(this);
};
element.addEventListener('click', someFunction, false);  // <div class="musician">David Gilmour</div>

someFunction(); // [object Window]

Changing this context

There are many reasons why we need to change the context of a function and we have a few methods that use can use to do it: .call(),.apply() and .bind().

You will use the methods above when you want this to refer to something different thant the scope it’s in.

With .call() and .apply() we can pass a new scope and arguments. They immediately invoke the function.

var Musician = function()
{
	this.getFullName = function() {
		console.log(this.firstName + ' ' + this.lastName);
	};
}

var GuitarMan = new Musician();
GuitarMan.getFullName(); //undefined undefined

var Gilmour = {
	firstName: "David",
	lastName: "Gilmour"
};
GuitarMan.getFullName.call(Gilmour); //David Gilmour
GuitarMan.getFullName.apply(Gilmour); //David Gilmour

Different from .call() and .apply() the .bind() method doesn’t invoke the function when it is called. It is used to setup the context for a function.

var Gilmour = {
	firstName: "David",
	lastName: "Gilmour",
	instrument: "Guitar"
};

var Musician = function()
{
	console.log(this);
}.bind(Gilmour);

Musician(); //Gilmour object

It can also be used in event handlers to add some extra information:

// .musician = <div class="musician">David Gilmour</div>
var Gilmour = {
	firstName: "David",
	lastName: "Gilmour",
	instrument: "Guitar"
};

var musician = document.querySelector('.musician');
var someFunction = function() {
	console.log(this);
};
element.addEventListener('click', someFunction.bind(Gilmour), false); // Gilmour

3. Closures

Private and public members in JavaScript is possible using closures. Closures treat functions as values, and as functions have his own scope, local variables are “re-created” every time a function is called. It emulates public and private scopes.

JavaScript have some design patterns, such as the Module pattern which can create a public and private scope. As we know that functions create scope, we can create a private scope by wrapping a function inside other function:

(function(){
	// here is a private scope
})();

We can add some functions for use:

(function(){
	var myFunction = function() {
		// your code here
	};
})();

Remember that the function is defined inside a private scope, so it isn’t possible to access it from the global scope:

(function(){
	var myFunction = function() {
		// your code here
	};
})();
myFunction(); // Uncaught ReferenceError: myFunction is not defined

So, that’s it! If we need the function to be public, we can now use the Module Pattern which allow us to scope our functions using private and public scopes and an Object. Basically, to turn a function public, we need to return a function or an Object.

Module Pattern

var Module = (function(){
	return {
		myPublicMethod: function() {
			console.log('myPublicMethod called');
		}
	};	
})();
Module.myPublicMethod();

So, follow this idea, everything that isn’t be returned is private.

var Module = (function(){
	var _myPrivateMethod = function() {
		console.log('myPrivateMethod called');
	};
	return {
		myPublicMethod: function() {
			console.log('myPublicMethod called');
		}
	};	
})();
Module.myPublicMethod();

You myght notice that JavaScript have a naming convetion to begin private methods with an underscore, which visually helps you differentiate between public and private methods. This allow us to return an anonymous Object simply assigning the function references.

var Module = (function(){
	var _myPrivateMethod = function() {
		console.log('myPrivateMethod called');
	};
	var publicMethod = function() {
		console.log('myPublicMethod called');
	};
	return {
		myPublicMethod: publicMethod
	};	
})();
Module.myPublicMethod();

4. Hoisting

Functions and variable declarations are always moved invisibly to the top of theirs containing scope by JavaScript interpreter. This JavaScript behaviour is called “hoisting”.

With variables, only the declaration name is hoisted. With function declaration, the entire function body will be hoisted. It doesn’t is applied for function expressions assigned to local variable. Follow the examples below:

console.log(fullName); // undefined
var fullName = "David Gilmour";
console.log(fullName); // David Gilmour

This will be interpreted like this:

var fullName;
    fullName = "David Gilmour";

As this is applied to the scope, lets take a look in this function scope.

function getFoo() {
	if(true)
	{
		var a = 1;
	}
	var b = 2;
	return;
}

Will be interpreted like this:

function getFoo() {
	var a, b;
	if(true)
	{
		a = 1;
	}
		b = 2;
	return;
}

Notice that only the name is hoisted, not the variables value. It isn’t he case with function declarations, where the entire function body will be hoisted as well.

function fooBar() {
	foo(); // TypeError "foo is not a function"
	bar(); // "bar() function"
	/*
		this is a `function expression` which isn't hoisted like `function declaration`.
	*/
	var foo = function () { 
		console.log("foo() function");
	}
	/*
		function declaration
	*/
	function bar() {
		console.log("bar() function");
	}
}
fooBar();

It is a good practice always declare your variables with just one var statement per scope and that it be at the top. Forcing yourself to do this, will be hard to get into some hoisting-related confusion.


Hope it helped you.


References