[TOC]
## previously
函數執行形成一個私有作用域,保護里面的私有變量不受外界干擾就叫閉包
### 作用域
[棧內存]
> 全局作用域:window
> 私有作用域:函數執行
> 塊級作用域:使用let創建變量,存在塊級作用域中
### 作用域鏈
當前作用域代碼執行的時候遇到一個變量,我們首先看一下它是否屬于私有變量,如果是當前作用域私有變量,那么以后在私有作用域中再遇到這個變量都是操作私有的(“閉包:私有作用域保護私有變量不受外界干擾”);如果不是私有的變量,向其上級作用域查找,如果也不是上級作用域私有的,就會繼續向上查找,直到找到window全局作用域,我們把這種向上一級級查找的機制叫做`作用域鏈`
全局下有,操作的就是全局變量,全局下沒有(設置:給全局對象window增加了屬性名&&獲取:報錯)
## 查找私有變量
>JS中的私有變量有且只有兩種
>- 在私有作用域變量提升階段聲明過的變量或則函數
>- **形參**也是私有變量
```
function fn(num1,num2){
var total = num1+num2;
return total;
}
var result = fn(100,200);
```
>函數執行形成一個全新獨立的私有的作用域
>1、 形參賦值
>2、 變量提升
>3、 代碼自上而下執行
>4、 當前棧內存(私有作用域)銷毀或則不銷毀
**注意:** 形參賦值在變量提升之前
```
var x=10
,y = 20
,z = 30;
function fn(x,y){
//=>[私有作用域]
//=>形參賦值:x=10 y=20 (x/y都是私有變量)
//=>變量提升:var x(忽略的,已經存在這個名字了)
console.log(x,y,z); //=>z不是私有變量,是全局變量
var x = 100; //=>私有的x=100
y = 200; //=>私有的y=200
z = 300; //=>全局的z=300
console.log(x,y,z); //=>100 200 300
}
fn(x,y,z); //=>FN執行傳的的是實參(實參都是值,不是指要轉換成值,這里傳遞的不是變量,而是變量的值傳過去了)
console.log(x,y,z); //=>10 20 300
```
FN執行傳的的是實參(實參都是值,不是指要轉換成值(**這個值可能是地址**),這里傳遞的不是變量,而是變量的值傳過去了)
```
//=>[私有作用域]
//=>形參賦值:b=1 (私有變量)
//=>變量提升:b=aaafff111(此處賦值操作替換了形參賦值的內容)
function fn(b){
console.log(b); //=>函數b本身
function b(){
//=>[私有作用域]
//=>形參賦值和變量提升都沒有
console.log(b);
}
b(); //=>函數b本身
}
fn(1);
```
## 如何查找上級作用域
```
var n = 10;
function sum(){
console.log(n);
}
// sum(); //=>10
~function(){
var n = 100;
sum(); //=>10 //sum的宿主環境(和箭頭函數的宿主環境定義貌似不一樣)是當前自執行函數形成的私有作用域,宿主環境和上級作用域沒有關系
}();
```
>函數執行形成一個私有作用域(A),A的上級作用域是誰,和它在哪執行沒有關系,主要是看他在哪定義的,在哪個作用域下定義的,當前A的上級作用域就是誰。
```
var n = 10;
var obj = {
n:20
,fn:(function(){
var n = 30;
//->上級作用域:全局作用域
return function(){
//->上級作用域:自執行函數
console.log(n);
}
})()
};
obj.fn(); //30//return function(){console.log(n);}
```
以上栗子,在堆內存中存obj的時候,遇到fn,發現是一個函數自執行,于是開始函數自執行,開辟一個棧內存,形參賦值,變量提升,逐行執行,然后return function,因為是function,此時會另外再開辟一個堆內存,返回堆內存的地址,作為return 返回的值。

```
var n = 10;
var obj = {
n:20
,fn:(function(n){
//->上級作用域:全局作用域
return function(){
//->上級作用域:自執行函數
console.log(n);
}
})(obj.n)
};
obj.fn(); //會報錯
```
## 閉包作用之保護
>函數執行形成的私有作用域會保護里面的私有變量不受外界干擾。
```
(function(window,undefined){
var jQuery = function(){
...
};
...
window.jQuery = window.$ = jquery;
})(window);
```
```
var Zepto = (function(){
var Zepto = function(){
...
};
...
return Zepto;
})();
Zepto();
```
在真實項目中,我們利用這種保護機制實現團隊協作開發。(避免了多人同一個命名,導致代碼沖突的問題)
## 閉包作用之保存
> 函數執行,形成一個私有作用域,函數執行完成,形成的這個棧內存一般情況下都會自動釋放。
>
>但是還有二般情況,函數執行完成,當前私有作用域中的某一部分內容被棧內存以外的其它東西(變量/元素的事件)占用了。當前的棧內存就不能釋放掉,也就形成了不銷毀的私有作用域(里面的私有變量也不會銷毀)。
```
function fn(){
var i = 1;
return function(n){
console.log(n+i++);
}
}
var f = fn();
f(10);//11
fn()(10);//11
f(20);//22
fn()(20);//21
```

>函數執行形成一個私有作用域,如果私有作用域中的的部分內容被外部的變量占用了,私有作用域不會銷毀。
>[形式]
>函數執行返回了一個引用數據類型堆內存的地址(并且這個堆內存是隸屬于這個作用域的),在外面有一個變量接收了這個返回值,此時當前作用域就不能銷毀(想要銷毀,只需要讓外面的變量賦值為null,也就是不占用當前作用域中的內容了)
### 有return不一定銷毀
私有作用域里沒有東西被占用,就會銷毀。
### 不銷毀不一定要有return
```
//這種形式私有作用域也不會銷毀
for(var i=0;i<oList.length;++i){
~function(i){
oList[i].onclick = function(){
changeTab(i);
}
//沒有return 但被事件綁定占用
}(i)
}
```
另外要注意,不是你return一個堆內存就不會銷毀(上面的例子并沒有return),而是要看return的這個是否被占用,如果沒有變量來接收,是直接會銷毀的
### 閉包保存在循環中的應用
```
//這樣達不到預期的效果
for (var i=0;i<oList.length;++i){
oList[i].onclick = function(){
changeTab(i); //=>不行的原因,給當前LI點擊事件綁定方法的時候,綁定的方法并沒有執行(點擊的時候才執行),循環3此,分別給3個LI的點擊事件綁定了方法,循環完成后i=3(全局的)。當點擊的時候,形成一個私有作用域,用到了變量i,i不是私有的變量,向全局查找,此時全局的i已經是最后循環的3了
}
}
//--- --- ---
for(var i=0;i<oList.length;++i){
oList[i].onclick = (function(i){
return function(){
changeTab(i);
}
})(i)
}
//--- --- ---
//這種形式私有作用域也不會銷毀
for(var i=0;i<oList.length;++i){
~function(i){
oList[i].onclick = function(){
changeTab(i);
}
}(i)
}
//--- --- ---
//塊級作用域 每次循環都會形成一個單獨的{}塊級作用域,每個塊級作用域之間沒有沖突
for (let i=0;i<oList.length;++i){
oList[i].onclick = function(){
changeTab(i);
}
}
```
- 空白目錄
- window
- location
- history
- DOM
- 什么是DOM
- JS盒子模型
- 13個核心屬性
- DOM優化
- 回流與重繪
- 未整理
- 文檔碎片
- DOM映射機制
- DOM庫封裝
- 事件
- 功能組件
- table
- 圖片延遲加載
- 跑馬燈
- 回到頂部
- 選項卡
- 鼠標跟隨
- 放大鏡
- 搜索
- 多級菜單
- 拖拽
- 瀑布流
- 數據類型的核心操作原理
- 變量提升
- 閉包(scope)
- this
- 練習題
- 各種數據類型下的常用方法
- JSON
- 數組
- object
- oop
- 單例模式
- 高級單例模式
- JS中常用的內置類
- 基于面向對象創建數據值
- 原型和原型鏈
- 可枚舉和不可枚舉
- Object.create
- 繼承的六種方式
- ES6下一代js標準
- babel
- 箭頭函數
- 對象
- es6勉強筆記
- 流程控制
- switch
- Ajax
- eval和()括號表達式
- 異常信息捕獲
- 邏輯與和或以及前后自增
- JS中的異步編程思想
- 上云
- 優化技巧
- 跨域與JSONP
- 其它跨域相關問題
- console
- HTML、XHTML、XML
- jQuery
- zepto
- 方法重寫和方法重載
- 移動端
- 響應式布局開發基礎
- 項目一:創意簡歷