0%

JS核心-(37)-函式以及This的運作-閉包進階:工廠模式及私有方法

前言

了解什麼是工廠模式及私有方法,針對不同的需求寫出不同的閉包 function


我們先來看看下面的例子,請問各位覺得會分別印出多少呢結果呢?

// 申論題
function  arrFunction () {
    var arr = [];
    for (var i = 0; i < 3; i++) {
        arr.push(function () {
            console.log(i);
        });
    }
    return arr;
}

var fn = arrFunction();
fn[0]();
fn[1]();
fn[2]();

可以看到都是印出 i = 3 的狀況

那麼我們先來檢查一下 fn 裡面是什麼

如同預期, fn 裡面是我們 push 進去的三段函式,而裡面參照的 i 很明顯是在 ( ) 父層宣告的變數。

那麼為什麼會都取得 3 呢 ? 在這個地方要思考一下, 我們在執行這個閉包的時候,父層的全域變數並非一成不便,

我們可以透過不同的方式去控制他,這個地方我們可以換一個方式檢視。

// 申論題
function  arrFunction () {
    var arr = [];
    for (var i = 0; i < 3; i++) {
        arr.push(function () {
            console.log(i);
        });
    }

    console.log('i', i); // 加這行
    return arr;
}

var fn = arrFunction();
fn[0]();
fn[1]();
fn[2]();

也就是說,在執行完這個 for 迴圈的時候呢, i 就已經是 3 了,而我們事後再進行呼叫的話,當然就都是印出 3 囉 !

那如果我們想要的是依序印出 0 1 2 的話該怎麼做 ?

可以利用到我們之前介紹到的立即函式 IIFE 進行修改,其中的一個功能就是限制作用域

// 申論題
function  arrFunction () {
    var arr = [];
    for (var i = 0; i < 3; i++) {
        (function (j) { // 改成 j
            arr.push(function () {
                console.log(j); // 改成印出 j
            });
        })(i); // 每次累加的 i 都傳入立即函式中
    }
    return arr;
}

var fn = arrFunction();
fn[0]();
fn[1]();
fn[2]();

透過這樣的方式就可以印出 0 1 2 結果

另外還有一種方式,是通過 ES6 let 的宣告函示的方法, 也可以印出 0 1 2 這樣結果的方式

// 申論題
function  arrFunction () {
    var arr = [];
    for (let i = 0; i < 3; i++) {
        arr.push(function () {
            console.log(i);
        });
    }
    return arr;
}

var fn = arrFunction();
fn[0]();
fn[1]();
fn[2]();

函式工廠

接下來看上一篇文章介紹到的例子

function storeMoney() {
    var money = 1000;
    return function (price) {
        money = money + price;
        return money;
    }
}

我們在父層的變數中 var money = 1000; 也不必固定一定要 1000,可以優化成預設變數的樣子

// 函式工廠
function storeMoney (initValue) {
    var money = initValue || 1000;
    return function (price) {
        money = money + price;
        return money;
    }
}

var MingMoney = storeMoney(100);
console.log(MingMoney(500));  // 600

像上方範例,將變數改為預設 initValue ,這樣又稱為函式工廠,透過這個函式工廠,你可以給他不同的值,但是又會做相同

的事情。


私有方法

閉包還有另外一種形式叫做私有方法

我們沿用上方的例子,我們執行這個 storeMoney 裡面回傳的子函式,但它都只會做一樣的事情,就是把傳入的數值累加,

但是實際狀況並不是只有加錢啊,小明應該也會想花錢吧!也會想知道現在有多少錢吧!

那我們該怎麼滿足這樣的需求呢 ? 就是利用私有方法定義不同的方法 !

// 函式工廠
function storeMoney (initValue) {
    var money = initValue || 1000;
    // 私有方法
    return {
        increase: function (price) {
            money += price;
        },
        decrease: function (price) {
            money -= price;
        },
        value: function (price) {
            return money;
        }
    }
}

我們在原本要回傳函式的地方 return 回傳了一個物件 { } ,物件中包含了許多可能會用到的私有方法,

以上方為例就是 increasedecreasevalue ,這樣很明顯,如果我們需要調用的時候呢,就可以

var MingMoney = storeMoney(100);

MingMoney.increase(100);
MingMoney.increase(100);
MingMoney.increase(300);
MingMoney.increase(290);

MingMoney.decrease(310);
console.log(MingMoney.value());

透過這樣的方式,我們就可以讓一個閉包的功能得到不同面向的實用性。

也可以針對不同的需求寫出不同的閉包 function,透過不同的分類來管理不同的辦法。