Scope

  1. Scope
  2. Hoisting
  3. Closure
  4. ES6

Scope is range or space to where variable and function will be stored.

Javascript has lexical scope which is defined at a lexing time and function-based scope (and block scope from ES6) and dynamic scope this .

Scope bubble is created during compilation time. Local variables will be stored on Scope Object (LexicalEnvironment). It is not allowed to access inner scope object from the outer scope. On the other hand, it is possible to access to outer scope from the inner scope. An outer scope is parent scope from inner scope which allows accessing outer scope from the inner scope. When the program accesses the variable, the JS interpreter searches the current scope object. If the interpreter can not find them, it goes to parent scope and returns it. This is called scope chain. It seems like prototype chain but it is slightly different. When interpreter cannot find the variable, it throws an error ReferenceError but undefined on the prototype chain.

Global scope is always at the end of the scope chain. If we declared a variable or a function on the top level, they will be on the global scope object.

Scope object lasts as long as the scope is referred. When referring to the scope object is no longer exist, garbage collection works and scope object is released. In javascript, invoking function is referring scope object.

Scope

You can see scope on this code.

var message = "Hello"

function sayHello(name){
  console.log(message + ", " + name);
}

sayHello("Kei"); // Hello, Kei

The variable and function delcared on top level is on global scope. There are message, sayHello() on the global scope.
What is like accessibilty between scope.

var message = "Hello" // Global Scope

function sayHello(name){
  console.log(message + ", " + name);
}

sayHello("Kei"); // Hello, Kei

sayHello is a function object and creates its own function scope. It is always referred outer scope. The ability to check outer scope is called scope chain. A deeply nested function may cause the deep search for the scope, which affects performance issue so it should be avoided as much as possible.

var message = "Hello"

function sayHello(name){
  var message = "Good bye"
  console.log(message + ", " + name);
}

sayHello("Kei"); // Hello, Kei

In this case, this refers own message variable, so that JS engine does not look for an outer scope. A callback can also look at an outer scope.

var message = "Hello"

var foo = function(){
  console.log(message);
}

function bar(fn){
  fn();
}

bar(foo); // Hello

Despite Inner scope can access outer scope, we can not access inner scope from outer one.

var message = "Hello"

var foo = function() {
  var userName = "Kei"
  console.log(message);
}


console.log(message); // Hello
foo(); // Hello
console.log(userName); // ReferenceError: userName is not defined

This is kind of tricky but we can use this feature for create private scope.

;(function foo(message){
  var userName = "Kei"
  function sayHello(message){
    console.log(message + " " + userName);
  }

  sayHello(message);
})("Hello"); // Hello Kei

sayHello("Hello") // ReferenceError: sayHello is not defined

This IIFE creates it own scope and sayHello function on out of IIFE can not be invoked because it is on outer scope of IIFE.
Being wrapped by IIFE is common technic to create scope which is good for privacy and avoiding global scope pollution.

These are basic scope and scope chain.


Hoisting

The concept of hoisting is simple. Declartion of variable and function goes to top of scope even you assign certain value to variable.

Sample code

code-1

foo() // foo is not a function

var foo = function(){  
}

Why this is not function? It is because var foo contains undefined at this moment.

code-2

foo() // foo is not defined

In this case, foo is not clearly defined because we did not declare any variable or function. Back to first code, only variable declartion is "hoisted"

code-1

foo() // foo is not a function

var foo = function(){  
}

code-1 is equivalant to code-3.

code-3

var foo; //undefined

foo(); // foo is not a function

foo = function(){  
};

To make this code work, we have to call method after assign function into variable foo.

code-4

var foo; //undefined

foo = function(){  
  console.log("foo")
};

foo(); // "foo"

Or Function Declaration

code-5

foo();

function foo(){
  console.log("foo")
}

Function Declaration is also hoisted which means you can call function anywhere inside scope

In the case of variable name collision

This is valid code.

var x = 0;

function hoge(){
  console.log(x); // 0
}

hoge();

However, this does not work because of hoisting and name collision.

var x = 0;

function hoge(){
  console.log(x); // undefined
  var x = 1; // var x declaration is hoisted
}

hoge();

Inside function scope, var declartion will be done at the begining.
This code is same as code below.

var x = 0;

function hoge(){
  var x;
  console.log(x); // undefined
  x = 1; // var x declaration is hoisted
}

hoge();

Function is hoisted

Hoisting is the reason why 'function declaration' is called first over 'function expression'.

foo(); // 1

var foo; // undefined

foo = function() {
    console.log( 2 );
};

// This function is hoisted to the top of scope.
function foo() {
    console.log( 1 );
}

http://wp-p.info/tpl_rep.php?cat=js-intermediate&fl=r9


Another Example

var salary = "1000$";

(function () {
  console.log("Original salary was " + salary);

  var salary = "5000$";

  console.log("My New Salary " + salary);
})();
var salary = "1000$";

(function () {
  var salary = undefined;
  console.log("Original salary was " + salary);

  salary = "5000$";

  console.log("My New Salary " + salary);
})();

What will the code below output to the console and why?

(function(){
  var a = b = 3;
})();

console.log("a defined? " + (typeof a !== 'undefined')); // true
console.log("b defined? " + (typeof b !== 'undefined')); // false (ES6 ReferenceError: b is not defined)
var a = b = 3;
// Wrong
var b = 3;
var a = b;

// Correct
b = 3;
var a = b;

Why should global scope never be touched?

As variables lose scope, they will be eligible for garbage collection. If they are scoped globally, then they will not be eligible for collection until the global namespace loses scope.


Closure

What is a closure, and how/why would you use one?

Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.

When you refer a function, you will also refer the scope of the function at the same time.

Let's see the code below.

var outter, inner;

function outterFunction(){

  var message = "Hello";

  function innerFunction(){
    console.log(message);
  }

  inner = innerFunction;

}

outter = outterFunction;
outter(); // undefined

inner(); // "Hello"

This code explains what "remember and access its lexical scope even when that function is executing outside its lexical scope" means.

function outterFunction() is declared and invoked. Then function innerFunction() is assined into var inner which is on the top level. In Javascript, outterFunction is called then scope will not be gone. Closure remembers and access its lexical scope.

How about multiple nested situation like below?

var a, b, c;

var name = "Kei";

function functionA(){

  var message = "Hello";

  function functionB(){

    console.log(message + " from function B");

    function functionC(){
      console.log(message + " from function C");
    }

    c = functionC;
  }

  b = functionB;

}

a = functionA;

a();
b();
c();

Despite multiple nested function, function remembers and is allowed to access to scope.

Use Memory?

  • Closures use memory, but they don't cause memory leaks since JavaScript by itself cleans up its own circular structures that are not referenced. Internet Explorer memory leaks involving closures are created when it fails to disconnect DOM attribute values that reference closures, thus maintaining references to possibly circular structures.

http://postd.cc/how-do-javascript-closures-work-under-the-hood/

var x = 10;

function foo() {
  alert(x);
}

(function (funArg) {

  var x = 20;

  // variable "x" for funArg is saved statically
  // from the (lexical) context, in which it was created
  // therefore:

  funArg(); // 10, not 20

})(foo);

This is also closure. The first variable x remember

Closure works in unexpected and wrong way.

Let's take a look this example.

<p id="help">Form</p>
<p>Email: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
    {'id': 'email', 'help': 'Your Email'},
    {'id': 'name', 'help': 'Your Fullname'},
    {'id': 'age', 'help': 'Your age'}
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help); // Closure works
    }
  }
}

setupHelp();

In this case, closure works in wrong way. Inside loop and onfocus function bind closure. Inside onfocus function item is last element since closure refers current index which is the last of index. The timing of assigning function and invoking function is different in this case. When you call onfocus event, the for loop is ended which means index is eqivalent to the last of index.

So what is closure used for?

closure is used all over the javascript but I would list some of concrete examples.

  1. Module (Create private accessor)

  2. When you use callback

  3. Practical closure (Curry)

1. Module

In some programming languages, if you keep privacy on some functions, you just write private or public explictly.

var module = function(name){

  var name = name;

  function getName(){
    console.log(name);
  }

  return {
    getName: getName
  };
}


var m = module("kei");

m.getName();

getName() function remembers the scope which is closure. var name can not be and retrive by public pethod getName().

Well, this code is almost similar to the code on private/public function section. We can apply closure to module.

var module = (function(option){

  var option = option || {};

  function getVersion(){
    console.log(option.version);
  }

  function changeRootPath(path){
    option.appPath = path;
  }

  return {
    getVersion: getVersion,
    changeRootPath: changeRootPath
  };

}(window.app.option || {}));


module.changeRootPath("/public/javascript/")

2. Callback

Closure works for callback very powerfully. Program can not access to outer scope without closure. Callback will be executed after outer function is invoked such as callback in ajax, setInterval or setTimeOut.

  • setInterval
var message = "Hello"

setInterval(function(){
  console.log(message)
}, 1000)
  • ajax

ajax callback is invoked after sending request which means the callback function seems separated to parent function. However, the function inside ajax has closure and allows to access to outer scope.

var userName = "Kei"
$.ajax({
  type: "GET",
  url: "http://localhost:3000",
  success: function(){
    console.log(userName);
  }
})

3. Practical closures / Curry

Practical closure is implemented with curry methology. The closure take a practical value of whole functionality but return function to implement rest of parts of functionality.

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

ES6

The lexical scope of a function is statically defined by the function’s physical placement within the written source code.

ES6 is block scope and works like this. The scope of var is based on function but let and const is based on block.

Block scope can not be accessed from outer scope like function scope.

function foo() {
  if(true) {
     let num = 0
  }
  console.log(num) // undefined
}

So this feature works to avoid unexpected closure creation like the case of for loop.

for(var i = 0; i < 5; i++) {
  setTimeout(function(){ console.log(i) }, 2000)
}
//(5 times) 5
for(let i = 0; i < 5; i++) {
  setTimeout(function(){ console.log(i) }, 2000)
}
// 0
// 1
// 2
// 3
// 4

One caveat of let is that let is hoisted but does not initialize it so it throws error if you invoke let

{
  console.log(foo);
  let foo;
}

// Uncaught ReferenceError: foo is not defined

var is hoisted and initialized so it has reference to it.

{
  console.log(foo);
  var foo;
}

// undefined

results matching ""

    No results matching ""