Introduction
The scope and context of javascript maybe the most bizarre point when learning javascript, for people who learn programming from C/C++/JAVA those traditional language will find the concept of scope and context are particularly difficult to understand. However, it’s essential for a javascript developer to understand these.
Global scope
When you starting write a javascript, there is a default global scope called Window. Have a look at the image below
When a variable is in global scope it can be access by any other scope
var name = "Albert Einstein";
console.log("1: ", name)
function print(){
console.log("2: ",name);
(function inner(){
console.log("3: ", name);
function inner2(){
console.log("4: ", name);
}
inner2();
})();
}
print();
## 1: Albert Einstein
## 2: Albert Einstein
## 3: Albert Einstein
## 4: Albert Einstein
As you can see above, it prints four names. the first print is in the global scope, the second print is in the print function scope and so for print 3 and print 4.
let vs var
Let’s discuss local scope or function scope, as we mentioned above, global
scope can be accessed everywhere, but local scope can only be accessed locally.
In ECMAScript 6, there are two keywords introduced. Which will make the variable
defined locally. let
is just like var
with local scope restriction and as
the name shows const
is a constant with local scope, when you try to modify a
constant variable the interpret will yelling.
if(1){
// global scope
var name = "Albert Einstein";
// local scope
let age = 140;
// local scope
const occupation = "scientist";
}
console.log(name); // can be accessed
console.log(age); // cannot be accessed
console.log(occupation); // cannot be accessed
Context
When we talking about context, we are referring this
keyword. Here is a definition
from MDN [1].
> In most cases, the value of this is determined by how a function is called
So, what we have to find is who called this function. If a object called a function
then this
is the object, if a button is called the function, then this
is
the button. Let’s see several examples
calling from window
console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
calling from an object
var obj = {
func: function(){
return this;
}
};
console.log(obj.func());// return an object {}
strict mode
if we don’t defind use strict
in the beginning of a javascript document. this
keyword will be default set to window in browser and global in nodejs.
without strict mode set
function foo(){
return this;
}
foo() === window; // true
setup strict mode
function boo(){
'use strict';
return this;
}
boo() === undefined; // true
It’s always a good practice to set the strict mode
Apply, Call, Bind
These three functions are quite useful, apply
and call
are similar. bind
is used to bind a function to another context. Here is an example showing that
how people can change the context to make the calling more dynamic
var test = {
prop: 42,
func: function() {
return this.prop;
},
};
var test2 = {
prop: 'Albert Einstein'
}
console.log(test.func()); // print 42
console.log(test.func.call(test2)); // print Albert Einstein
like a magic right? Here is the different use of apply
, call
and bind
func.call(context, "arg1", "arg2", "argn");
func.apply(context, ["arg1", "arg2", "argn"]);
func.bind(context);
call has slighly faster in performance than apply [2]
Public and Private
The function scope can be used to mimic traditional OOP language properties like private variables. Moreover, it can be used to manage namespace.
// only expose AwesomeModule keyword globally
var AwesomeModule = (function(){
// private variable and methods
let property = "property";
function privateFunc(){
...
}
// you can access the method by AwesomeModule.publicFunc()
// this kind format more like OOP
return {
publicFunc: function(){
// can access property and privateFunc()
}
}
})()
the code above has a small bizarre format (function() {})()
. This is called
immediately invoked function expression (IIFE). How to interpret this code? It’s
simple, you define a function like this function(){}
, you want to invoke it
after define it the you just add an parenthesis ()
. However, in order to define
this function you have to use another parenthesis enclose them, that’s all.
Closure
Closure is quite useful, like another magic in javascript. We know when a function return the variables define in that scope will be destroied or collected, but closure is not working that way, even the function returned, closure can still keep the variable. Let’s see an example
function outer(outerArg){
let property = "property";
return function(innerArg){
console.log(outerArg);
console.log(property);
console.log(innerArg);
};
}
// notice outer has returned, property should be destroied
// but closure not working that way
var func = outer("from outer function");
// what we returned is not a variable is a function, we have to invoke the function
func("from inner function");
// output
// from outer function
// property
// from inner function
The example above shows that closure can not only access its own arguments, but the arguments from its outer functions and the returned function will not be invoked immediately, you can invoke the returned function whenever you want. Which is really handy. There is a very good visualization tool created by Tyler McGinnis. Which gives a really concrete example of what closure is [1]
References
1. The Ultimate Guide to Hoisting, Scopes, and Closures in JavaScript [Internet].TylerMcGinnis.com2019;Available from: https://tylermcginnis.com/ultimate-guide-to-execution-contexts-hoisting-scopes-and-closures-in-javascript
2. Understanding Scope in JavaScript [Internet].
Ahmed H.
Scotch2019;Available from: https://scotch.io/tutorials/understanding-scope-in-javascript