DAY 23
1
Modern Web

• 範圍鏈概念
• 理解閉包概念的例子
• 函式工廠、私有化

## 範圍鏈(Scope chain)

• 內層函式可以取得外層函式的變數，但外層函式就不能取得內層函式的變數
• 如果在內層函式裏，找不到某個變數，就會往上層查找，直至找到為止，最後會找到全域

``````function funcA(){
var num = 10;

function funcB(){
//內層沒有num，往上層找num
var x = 100;
console.log(num) //10
}
funcB()

//外層不能訪問內層
console.log(x) //x is not defined
}

funcA()
``````

## 閉包概念

1. 把內層函式`inner`存在一個變數`a`
2. 呼叫變數`a`
``````var name = '全域name'
function outer(){
name = '區域name'
function inner(){
console.log(name)
}
return inner
}

const a = outer()
a() //區域name
``````

`inner`裏的`name`，會取得外層的`name`，即是`區域name`，這個範圍鏈已經定義好了。即使你之後在全域呼叫此內層函式，它裏面的`name`還是會指向`outer`裏的`name`(`區域name`)

## 理解閉包概念的例子

### 例子一

``````function func(){
var arr = [];
for(var i=0; i<3; i++){
arr.push( function(){
console.log(i);
})
}
return arr;
}

var result = func();
result[0]()
result[1]()
result[2]()

//回傳3,3,3
``````

``````[function(){console.log(i);}, function(){console.log(i);}, function(){console.log(i);}]
``````

``````function func(){
var arr = [];
for(var i=0; i<3; i++){
(function(i){
arr.push( function(){
console.log(i);})
})(i)
}
return arr;
}

var result = func();
result[0]()
result[1]()
result[2]()

//回傳0,1,2
``````

### 例子二

``````function counter(){
var count = 0;
return function(){
return ++count
}
}

var countFunc = counter();

console.log(countFunc()); //1
console.log(countFunc()); //2
console.log(countFunc()); //3
``````

``````function counter(){
var count = 0;

function innerCounter(){
return ++count;
}

var tmpV = innerCounter();
// console.log( '>>>' + tmpV ); (這行無關重要所以先註解起來)
return tmpV;
}
console.log( counter() );   // 1
console.log( counter() );   // 1
console.log( counter() );   // 1
``````

Kuro做法：

``````把回傳的內層函式存放到變數 > 呼叫變數(呼叫內層函式) > 得出數值
``````

``````呼叫外層函式 > 把內層函式結果存到變數 > 回傳變數 > 得出數值
``````

``````function counter(){
var count = 0;
return function(){
console.log('上一次的數目：' + count)
return ++count
}
}
var countFunc = counter();

console.log(countFunc()); //1
console.log(countFunc()); //2
console.log(countFunc()); //3
``````

## 函式工廠

``````function calculate(init){
//預設價錢是100
var price = init || 100;
return function(num){
price += num;
return price;
}
}

var item1 = calculate(500);
var item2 = calculate(2000);
var item3 = calculate();

console.log(item1(50)) //550
console.log(item2(1000)) //3000
console.log(item3(10)) //110
``````

``````var item1 = calculate(500);
var item2 = calculate(2000);
var item3 = calculate();
``````

``````給它不同原材料(值) > 做一樣的流程 > 生產不同結果
``````

## 私有化

``````function calculate(init){
//預設價錢是100
var price = init || 100;
return {
return price += num
},
deduct: function(num){
return price += num
}
}
}

var item1 = calculate(500);
var item2 = calculate(2000);

console.log(item2.deduct(500)); //2500
``````

## 總結

• 閉包是指當我們回傳內層函式時，除了回傳函式本身，也會回傳內層函式當時環境的變數值，以及記下當時的環境
• 範圍鏈在建立函式時已經定義好，不是在呼叫函式時定義
• 閉包最常用在巢狀函式，即是函式裏的函式
• 閉包強大功能是可以把函式裏的資料狀態保存下來

## 參考資料

JavaScript基本功修煉31