您的位置:首页 > Web前端 > JavaScript

Note On <You Don't Know JS - Scope and Closures>

2014-07-30 10:16 561 查看





Chapter 2: Lexical Scope

Some highlights:

All the optimizations Javascript engine would make are invalid if eval() or with are present, so the engine just simply does NOT perform any optimization at all. That's why the two utilities are frowned up in the community as
bad practices.
setTimeout(), setInterval() and new Function() are also said to be bad practices,but what is the alternative to setTimeout()?

Chapter 3: Functions Versus Block Scope

Some highlights:

By using function expression, you can limit the function name identifier to reside only in that function's own scope.
var a = 2;

(function foo(){ // <-- insert this
var a = 3;
console.log( a ); // 3
})(); // <-- and this

console.log( a ); // 2


Function Expression VS. Function Declaration

IIFE (immediately-invoked function expression)
var a = 2;
(function foo(){ // <-- insert this
var a = 3;
console.log( a ); // 3
}()); // <-- and this

console.log( a ); // 2
var a = 2;
(function IIFE( global ){
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
})( window );
console.log( a ); // 2
var a = 2;
(function IIFE( def ){
def( window );
})(function def( global ){
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
});

It is always better to provide a name in function expression, therefore the author would NOT recommend anonymous callback function;

'catch' keyword creates a block scope;
use 'let' keyword to create block scope, which is relevant to closures and garbage collection;

var foo = true;
if (foo) {
let bar = foo * 2;
console.log( bar );
}
console.log( bar ); // ReferenceError
var foo = true;
if (foo) {
{ // <-- explicit block
let bar = foo * 2;
console.log( bar );
}
}
console.log( bar ); // ReferenceError
{
console.log( bar ); // ReferenceError!
let bar = 2;
}


let keyword is not supported by Chrome, or IE 10.





'const' is supposed to create block-level variable

var foo = true;
if (foo) {
var a = 2;
const b = 3; // block-scoped to the containing `if`
a = 3; // just fine!
b = 4; // error!
}

console.log( a ); // 3
console.log( b ); // ReferenceError! , nothing in Firefox


It is different in practice than what is said in this book, in both Chrome and Firefox, 'const' makes the variable accessible from the out-containing scope, instead of making it a local block variable.
And in IE, it even throws an error:



Chapter 4: Hoisting

a = 2;
var a;
console.log( a );	// 2

////////////////////////
console.log( b_V );	// undefined
var b_V = 2;


Some highlights:

If the statement is like: var boo = function(){}, then it will be interpreted as two separate parts: declaration & assignment, and only the declaration will be hoisted.
/* only declaration is hoisted, but function expression */

foo_V_1(); // not ReferenceError, but TypeError!
var foo_V_1 = function bar() {
// ...
};
/* name identifier in function expression is not hoisted */

foo_V_2(); // TypeError
bar_V_2(); // ReferenceError
var foo_V_2 = function bar_V_2() {
// ...
};

Otherwise, if the statement is a function expression like: function boo(){}, it will be hoisted as a whole.
foo_V_0();
function foo_V_0() {
var c_V = 2;
console.log( c_V );
}

If the same name identifier is assigned to with a variable and appears in a function expression as well, then the one for function will be hoisted, and the statements assigning other values to the identifier as variable will
be ignored.
/*Function First*/

foo_V_3(); // 1
var foo_V_3;	// should be hoisted, but it is variable, so it is omitted
function foo_V_3() {	// should be hoisted, and it is function, so it is hoisted firstly
console.log( 1 );
}
foo_V_3 = function() {	// will override the previous definition
console.log( 2 );
};

However, if there is other statements assigning function expressions to the identifier follows, then function definition will be overridden.
foo(); // 3
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}

Function declaration will be hoisted to the enclosing scope, that may across the block, but the sample code fails to run in Firefox.
foo_V_5(); // "b"
var a_V_5 = true;
if (a_V_5) {
function foo_V_5() { console.log("a"); }
}
else {
function foo_V_5() { console.log("b"); }
}
//Throw ReferenceError: foo_V_5 is not defined in Firefox


Chapter 5: Scope Closure

Some highlights:

The function is being invoked well outside of its author-time lexical scope. Closure lets the function continue to access the lexical scope it was defined in at author time.

function foo() {
var a = 2;
function baz() {
console.log( a ); // 2
}
bar( baz );
}

function bar(fn) {
fn();
}
var fn;

function foo() {
var a = 2;
function baz() {
console.log( a );
}
fn = baz; // assign baz to global variable
}

function bar() {
fn();
}

foo();
bar(); // 2

Module pattern
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];

function doSomething() {
console.log( something );
}

function doAnother() {
console.log( another.join( " ! " ) );
}

return {
doSomething: doSomething,
doAnother: doAnother
};
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
To state it more simply, there are two requirements for the module pattern to be exercised:

There must be an outer enclosing function, and it must be invoked at least once (each time creates a new module instance).
The enclosing function must return back at least one inner function, so that this inner function has closure over the private scope, and can access and/or modify that private state.
Module as singleton.
var foo = (function CoolModule() {
var something = "cool";
var another = [1, 2, 3];

function doSomething() {
console.log( something );
}

function doAnother() {
console.log( another.join( " ! " ) );
}

return {
doSomething: doSomething,
doAnother: doAnother
};
})();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

Passing parameter when creating module.
function CoolModule(id) {
function identify() {
console.log( id );
}

return {
identify: identify
};
}

var foo1 = CoolModule( "foo 1" );
var foo2 = CoolModule( "foo 2" );

foo1.identify(); // "foo 1"
foo2.identify(); // "foo 2"

Singlton design with parameters.
var foo = (function CoolModule(id) {

function change() {
// modifying the public API
publicAPI.identify = identify2;
}

function identify1() {
console.log( id );
}

function identify2() {
console.log( id.toUpperCase() );
}

var publicAPI = {
change: change,
identify: identify1
};

return publicAPI;
})( "foo module" );

foo.identify(); // foo module
foo.change();
foo.identify(); // FOO MODULE


APPENDIX

Some highlights:

Javascript does NOT support dynamic scope, which means to care about where the functions are called from rather than how and where they are declared
function foo() {
console.log( a );	// 2
}

function bar() {
var a = 3;
foo();
}

var a = 2;
bar();


You may be confused and mix closure up with dynamic scope, try the following snippets may help to clear it up:
function foo() {
console.log( a );	// ReferenceError: a is not defined
}

function bar() {
var a = 3;
foo();
}

bar();


function bar() {
var a = 3;
function foo() {
console.log( a );	// 3
}
foo();
}
var a = 2;
bar();


function bar() {
var a = 3;
function foo() {
console.log( a );	// 3
}
foo();
}
bar();

The trick to use block scope in pre-ES6 environments
try {throw 2} catch(a){
console.log( a ); // 2
}
console.log( a ); // ReferenceError, but in Chrome & Firefox I got 2,
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐