iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 9
1

閉包 closure

function的執行依賴於變數的作用域,這個作用域是在function定義時決定,並不是在被呼叫時決定的。
function物件內部包含代碼邏輯外,還必須引用當前作用域鏈,function可透過作用域鏈相互連接起來,function內部的變量都可以保存在function作用域內。

  • 以下例子
    fn()執行時會得到fn的function,然後再()執行fn_1(在外面才執行)。
var scope= "global"
function fn(){
    var scope= "local"
    function fn_1(){return scope;}
    return fn_1;
}

fn()(); // "local" 
// 等同
var call_fn= fn(); // call_fn = fn_1
call_fn(); // 執行fn_1(); 

可以看出上面兩段程式碼,一個是直接回覆fn計算完的結果(在fn裡面執行fn_1),另一個是在fn外執行fn_1,但最後顯示的訊息都是一樣的。

  • 運用私有狀態
    下面的例子沒有宣告區域變數,而是透過參數n來保存accessor方法共享的私有狀態
function counter(n){ n 為函式內私有變數
    return{ 
        get count(){return n++;},
        set count(m){ n = m ;}
    }

}
var c = counter(10);
c.count // return 10
c.count // return 11
c.count = 1000 // 此時counter裡面的n為1000
c.count // 1000

特殊的例子

下面兩個例子

  • fn_a
    ary_a與i 都是全域變數
function fn_a(v){
    return function() {return v;}
}
var ary_a =[];
for(var i = 0 ; i<10 ; i++){
    ary[i] = fn_a(i);
}
// 上述的for迴圈執行完後
// ary[0] = function() {return 0;}
// ary[1] = function() {return 1;}
// ...
ary[5]() // 5
  • fn_b
    ary_b與j屬於fn_b的私有變數,closure關聯的scope chain巢狀函式(for...)不會製作scope的私有副本或製作變數繫結的靜態快照。
function fn_b(v){
    var ary_b =[];
    for(var j = 0 ; j<10 ; j++){
        ary_b[j] = function (){ return j; }
    }
    return ary_b;
}

// 上面的程式碼等同於
// function fn_b(v){
//     var ary =[];
//     var i;
//     for(i = 0 ; i<10 ; i++){
//         ary[i] = function (){return i;}
//     }
//     return ary;
// }
var test = fn_b();

test[5]() // 10

探討一下fn_b例子的for迴圈
由下表可test0~test10 全部都會是10

i ary
0 ary[0] = function(){return i;}
1 ary[0] = function(){return i;}
ary[1] = function(){return i;}
2 ary[0] = function(){return i;}
ary[1] = function(){return i;}
ary[2] = function(){return i;}
... ...
9 ary[0] = function(){return i;}
ary[1] = function(){return i;}
ary[2] = function(){return i;}
...
ary[9] = function(){return i;} // 此時i =10
... ...

call() / apply()

call()和apply()都是間接的呼叫函式(function)。兩者的第一個引數都是用來定義函式內this要指向誰。
call()和apply()兩者作用大致上是一樣的,僅差在輸入的引數
fn.call(this,x,y,z,...)
fn.call(this,[x,y,z,...]) 第二個引數為陣列

function fn_a(){
    this.name = 'Jackson';
    this.id = 1;
}

function fn_b(){
    this.name = 'Jack';
    this.print = function(){
        console.log(this.name + ' is a superman.')
    } 
}

var b= new fn_b;
b.print(); // Jack is a superman

var a = new fn_a

b.print.call(a); // 將a(第一個引數)傳入即代表在print函式裡頭的this是指向a裡頭的name
// Jackson is a superman.

bind()

與call、apply雷同,第一個引數一樣是定義函式內部的this指向。
call() apply()屬於立即函式,但bind()會回傳函式,需要再呼叫才會執行。

var a = {name:'Jackson'}
function fn_c(){
    console.log(this.name + ' is a superman!')
}

var cb = fn_c.bind(a);
cb() // "Jackson is a superman!    "

上一篇
Day 8: 函數 function (Part 1)
下一篇
Day 10: 類別與模組(Part 1)
系列文
Javascript 犀牛本-濃縮再濃縮 提煉再提煉30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言