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

Monday, January 16, 2017

A better way to search an Object in an Array

You can use the Array.prototype.map() to create a shadow array, which will help you search more fast, or furthermore you can create an index object like below, it will directly return the index you need.

var cars = [
    { id:23, make:'honda', color: 'green' },
    { id:36, make:'acura', color:'silver' },
    { id:18, make:'ford', color:'blue' },
    { id:62, make:'ford', color:'green' }, 
];
let cars_s = cars.map(function(x) {
    return x.id;
});
let i = cars_s.indexOf(18);
console.log(i); // 2
console.log(cars[i]); // { id:18, make:'ford', color:'blue' }

let index = {};
for (let i = 0; i < cars_s.length; i++) {
    index[cars_s[i]] = i;
}
console.log(index[18]); // 2

@reference_1_stackoverflow
 

Arrow functions

An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target. Arrow functions are always anonymous. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

@reference_1_mozilla
@reference_2_mozilla

Positive & Negative Lookahead

^(?!^0+$)\d{1,4}
 
@reference_1_stackoverflow
@reference_2_stackoverflow
@reference_3_stackoverflow

Sunday, January 15, 2017

Object Clone & Closure & Date

Some built-in objects have properties hidden in a closure(namespace), such as Date/String objects. So if you clone an object by just recursively copying all the enumerable or non-enumerable variables/methods into an empty object, it won't work, even including its prototype. It is impossible to detect the private variables(closure) in an object in Javascript.

A lot of people (including me) who try to write the deep clone method will finally find it almost impossible:
@reference_1_stackoverflow

Monday, January 9, 2017

window & Window.prototype

console.log(Window.prototype.__proto__.__proto__.__proto__ === Object.prototype); 
// true (Firefox & Chrome)
console.log(Window.prototype.__proto__ === Object.prototype);   
// true (IE11)

console.log(window instanceof Window);     // true console.log(Window.prototype.isPrototypeOf(window));    // true

Thursday, January 5, 2017

toSource() & toString() & eval() -- function to string & string to function

The toSource method returns the following values:
For the built-in Function object, toSource() returns the following string indicating that the source code is not available: 
function Function() { 
    [native code] 
} 
For custom functions, toSource() returns the JavaScript source that defines the object as a string.

@reference_1_mozilla

eval as a string defining function requires "(" and ")" as prefix and suffix

var fctStr1 = "function a() {}"
var fctStr2 = "(function a() {})"
var fct1 = eval(fctStr1)  // return undefined
var fct2 = eval(fctStr2)  // return a function

@reference_2_mozilla

Wednesday, January 4, 2017

JavaScript closure inside loops

@reference_1_stackoverflow

@reference_2_stackoverflow

Function.prototype.bind()

Function.prototype.bind()
@reference_1_mozilla

Object.prototype.toSource()
@reference_2_mozilla
uneval()
@reference_3_mozilla

Enumerability & ownership of properties

@reference_1_mozilla

Object.prototype.propertyIsEnumerable()
@reference_2_mozilla

Object.getOwnPropertyNames()
@reference_3_mozilla
Is it possible to simulated Object.getOwnPropertyNames in IE8?
@reference_5_stackoverflow

Object.defineProperty()
@reference_4_mozilla

Object.getPrototypeOf() & Object.prototype.__proto__

Object.getPrototypeOf(obj)

The prototype of the given object. If there are no inherited properties, null is returned.

var proto = {};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true

Opera-specific notes
Even though older Opera versions don't support Object.getPrototypeOf() yet, Opera supports the non-standard __proto__ property since Opera 10.50.

@reference_1_mozilla

Object.prototype.__proto__

Warning: While Object.prototype.__proto__ is supported today in most browsers, its existence and exact behavior has only been standardized in the ECMAScript 6 specification as a legacy feature to ensure compatibility for web browsers. For better support, it is recommended that only Object.getPrototypeOf() be used instead.

 @reference_2_mozilla
@reference_3_2ality

Object.prototype.constructor.prototype ?