YDKJ 讀書筆記 01 Function vs. Block Scope

Introduction

本系列文章爲You Don't Know JS的讀書筆記。
書籍地址:https://github.com/getify/You-Dont-Know-JSgit

Scope From Functions

一個很是廣泛的觀點是,Javascript的做用域是基於函數的,這個觀點其實並非那麼正確,不過,讓咱們來先看一下函數級別的做用域。github

function foo(a) {
    var b = 2;
    // some code
    function bar() {
        // ...
    }
    // more code
    var c = 3;
}

foo函數中有4個標識符,a、b、c、bar。這些標識符在什麼位置定義並不重要,變量或者是函數,都將歸屬於當前的做用域。
bar()也有與之對應的做用域,全局做用域也是如此(僅有一個表示符foo與它關聯)。
a,b,c,bar都隸屬於foo(..)做用域,在foo(..)的外部,是沒法操做到它們的。在全局做用域下寫以下代碼:會拋出ReferenceErrorexpress

bar(); // fails
console.log( a, b, c ); // all 3 fail

Functions As Scopes

使用函數做爲代碼的封裝,有一些細節上的問題:咱們使用foo(..)封裝代碼時,foo自己也污染了做用域。若是咱們只是爲了隱藏細節,不是爲了抽象代碼邏輯,那麼,這是一個解決方案:函數

(function foo(){ 
    // Coding here
})();

這樣作,foo是污染不了全局做用域的(也許咱們更願意在這種狀況下采用匿名版本)。工具

(function (){ 
    // Coding here
})();

這裏說一下使用匿名函數的兩個壞處:沒法在stack traces中識別到他們、低代碼可讀性。
因此除了較短的邏輯,儘可能不要用匿名函數。code

Invoking Function Expressions Immediately

剛纔咱們經過把函數包裝在(..)中,再當即調用,被稱爲Immediately Invoked Function Expression,簡稱爲IIFE,爲了不匿名函數的壞處,IIFE能夠這樣命名ip

var a = 2;
(function IIFE(){

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

})();
console.log( a ); // 2

關於客戶端的IIEF,常常能夠看到這樣的代碼,傳window,作些事情。作用域

var a = 2;
(function IIFE( global ){

    var a = 3;
    console.log( a ); // 3
    console.log( global.a ); // 2
})( window );

還有這樣的,比較暗黑的作法(防止某變量被外界覆蓋,好比undefined):get

undefined = true; // setting a land-mine for other code! avoid!

(function IIFE( undefined ){
    var a;
    if (a === undefined) {
        console.log( "Undefined is safe here!" );
    }
})();

Blocks As Scopes

for (var i=0; i<10; i++) {
    console.log( i );
}
console.log(i);

不少從其它語言切換切換到js,不能理解以上的代碼竟然能夠輸出10。
其它語言經常使用到的塊級做用域,在js上彷佛沒有,其實否則。
try/catchit

try {
    undefined(); // illegal operation to force an exception!
}
catch (err) {
    console.log( err ); // works!
}
console.log( err ); // ReferenceError: `err` not found

注意到沒有,catch是能拿到err,可是log卻拿不到。

雖然這是ES3的規範,可是不少語法驗證工具會在有多個try/catch,且catch的形參都爲err或者其它相同的名字時,會報re-definition的warning,不用管。

let
ES6提供了新的關鍵字let。

var foo = true;
if (foo) {
    let bar = foo * 2;
    bar = something( bar );
    console.log( bar );
}
console.log( bar ); // ReferenceError

塊級風格。

for (let i=0; i<10; i++) {
    console.log( i );
}
console.log( i ); // ReferenceError

須要注意的是,若是使用let

{
   console.log( bar ); // ReferenceError!
   let bar = 2;
}

在這一點上,和var是不同的。

const

const也是ES6提供的新關鍵字,除了定義出的變量初始化後不可更改外,其它與let一致。

相關文章
相關標籤/搜索