Wednesday, January 18, 2017

Function expression hoisting

Function expressions in JavaScript are not hoisted, unlike function declarations. You can't use function expressions before you declare them:
notHoisted(); // TypeError: notHoisted is not a function

var notHoisted = function() {
   console.log("bar");
};

Named function expression

If you want to refer to the current function inside the function body, you need to create a named function expression. This name is then local only to the function body (scope). This also avoids using the non-standard arguments.callee property.

var math = {
  'factorial': function factorial(n) {
    if (n <= 1)
      return 1;
    return n * factorial(n - 1);
  }
};

Tuesday, January 17, 2017

Lexical closures *

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();
In languages without lexical closures, the local variables within a function only exist for the duration of that function's execution. Once makeFunc() has finished executing, it is reasonable to expect that the name variable will no longer be accessible. Since the code still works as expected, this is obviously not the case in JavaScript.

The reason is that functions in JavaScript form closures. A closure is the combination of a function and the lexical environment (or simply "environment") within which that function was declared. The environment consists of any local variables that were in-scope at the time that the closure was created. In this case, myFunc is a reference to the instance of the function displayName created when makeFunc is run. The instance of displayName maintains a reference to its lexical environment, within which the variable name exists. Hence when invoking myFunc, the variable name remains available for use and "Mozilla" is passed to alert.

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12
In essence, makeAdder is a function factory — it creates functions which can add a specific value to their argument. In the above example we use our function factory to create two new functions — one that adds 5 to its argument, and one that adds 10.

add5 and add10 are both closures. They share the same function body definition, but store different environments. In add5's environment, x is 5. As far as add10 is concerned, x is 10.

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};

var counter1 = makeCounter();
var counter2 = makeCounter();
alert(counter1.value()); /* Alerts 0 */
counter1.increment();
counter1.increment();
alert(counter1.value()); /* Alerts 2 */
counter1.decrement();
alert(counter1.value()); /* Alerts 1 */
alert(counter2.value()); /* Alerts 0 */
Notice how each of the two counters maintains its independence from the other. Its environment during the call of the makeCounter() function is different each time. The closure variable privateCounter contains a different instance each time. (similar as recursion)

Using closures in this way provides a number of benefits that are normally associated with object oriented programming, in particular data hiding and encapsulation.@reference_1_mozilla

IIFE: Immediately-Invoked Function Expression & 模块模式

# 模块模式
### 闭包与 IIFE (Immediately-Invoked Function Expression)
模块模式使用了 JavaScript 的一个特性,即闭包(Closures)。现今流行的一些 JS 库中经常见到以下形式的代码:
;(function (参数) {
  // 模块代码
  // return something;
})(参数);
上面的代码定义了一个匿名函数,并立即调用自己,这叫做自调用匿名函数(SIAF),更准确一点,称为立即调用的函数表达 (Immediately-Invoked Function Expression, IIFE–读做“iffy”)。
 
在闭包中,可以定义私有变量和函数,外部无法访问它们,从而做到了私有成员的隐藏和隔离。而通过返回对象或函数,或是将某对象作为参数传入,在函数体内对该对象进行操作,就可以公开我们所希望对外暴露的公开的方法与数据。
这,其实就是模块模式的本质。

注1:上面的代码中,最后的一对括号是对匿名函数的调用,因此必不可少。而前面的一对围绕着函数表达式的一对括号并不是必需的,但它可以用来给开发人员一个指示 -- 这是一个 IIFE。也有一些开发者在函数表达式前面加上一个惊叹号(!)或分号(;),而不是用括号包起来。比如 knockoutjs 的源码大致就是这样的:
!function (参数) {
  // 代码
  // return something
}(参数);
还有些人喜欢用括号将整个 IIFE 围起来,这样就变成了以下的形式:
 (function (参数) {
  // 代码
  // return something
}(参数)); 

@reference_1_qnimate
@reference_2_stackoverflow
@reference_3_oschina *
@reference_4_stackoverflow
@referenece_5_stackoverflow

functions & their own lexical environment

function foo() {
  return function inner() {
    console.log(bar);
  }
}
function outer() {
  var bar = 0;
  var fakeInner= foo();  
  // not the same as "var fakeInner= function () { console.log(bar); }"
  fakeInner();
}
outer(); // ReferenceError: bar is not defined 
Functions do only have access to their own lexical environment - from where they were defined. JavaScript does not have dynamic scope where a function has any kind of access to the scope where it is called.
This happens by attaching the scope to the function object, it is a part of the closure.
@reference_1_stackoverflow
@reference_2_wikipedia
@reference_3_ stackoverflow