[TOC]
## 變量提升
在**當前**作用域當中,把所有帶var和function關鍵字的提前聲明或則定義
### 聲明和定義
> 在`當前作用域`中,js代碼自上而下執行之前,瀏覽器首先會把所有帶`var/function`關鍵字的進行提前的`聲明或則定義`
es6中的`let/const`并不會
>聲明(declare): var num;在當前作用域中吼一嗓子 我有num這個名了
> 定義(defined):num=12;把聲明的名字賦一個值(先有個值,再把這個值和聲明的名字建立鏈接)
### var和function
>[warning]帶var關鍵字的只是提前的聲明一下;帶function關鍵字的在變量提升階段把聲明和定義都完成了
```
console.log(num);
console.log(fn);
var numb = 13;
function fn(){
console.log(a);
var a =10;
console.log(a);
}
fn();
console.log(numb)
```

> **注意:** 除了全局作用域執行時,函數執行時也會執行自己的變量提升
### 帶var和不帶var的區別
在項目中,如果目的是創建變量,最好不要省略var
#### 帶var是聲明變量(順道在全局下定義一個屬性)
在當前作用域中聲明一個變量,如果當前是全局作用域,也相當于給全局作用域設置了一個屬性叫做a。
```
//=>變量提升:var a; <=> window.a=undefined;
console.log(a); //->undefined
var a = 12; //定義了一個變量a并且將12這個值賦給了它,另外順道在 window.a=12
console.log(a); //->12
console.log(window.a); //->window['a'] 在”全局作用域“中,我們聲明一個變量相當于給全局對象window增加了一個屬性名
```
#### 不帶var是全局屬性
在全局作用域中,如果不帶var,僅僅只是給全局對象設置了一個新的屬性名(相當于window.a=xx)
```
console.log(a); //=>Uncaught ReferenceError:a is not defined
//先看是不是變量,如果沒有,在window下看有沒有這個屬性,如果都沒有,就會報錯(瀏覽器會認為你在忽悠它)
//如果輸出的window.a,不會報錯,只會輸出undefined
a = 12; //<=>window.a = 12
console.log(a); //=>12
console.log(window.a); //=>12
```
### 只對等號左邊的進行變量提升
~~~
等號=
左邊是變量
右邊都應該是值
~~~
匿名函數:函數表達式(把函數當做一個值賦值給變量或則其它內容)
```
oDiv.onclick = function(){
}
//<=>
oDiv.onclick = aaafff000
```
變量提升只會提升等號左邊的,右邊是值不會提前聲明。
```
console.log(fn);//undefined
var fn = function(){
}
console.log(fn);//->函數本體
//谷歌瀏覽器中用小f 表示一個function
```
```
sum(); //報錯 Uncaught TypeError:sum is not a function
var sum = function(){}
```
真實項目中,應用這個原理,我們創建函數的時候可以使用函數表達式的方式。
為了在項目中使代碼更加嚴謹,我們一般都通過函數表達式的方式來定義一個函數(先定義后執行)
因為:
1. 只能對等號左邊的進行提升,所以變量提升完成后,當前函數只是聲明了,沒有定義,想要執行函數只能放在賦值的代碼之后(放在前面執行相當于讓undefined執行,會報錯的)
2. 這樣讓我們的代碼邏輯更加嚴謹,以后想要知道一個執行的函數做了什么功能,只需要向上查找定義的部分即可(不會存在定義的代碼在執行下面的情況)
```
var fn = function sum(){
console.log(sum);//->函數本身
console.log(1);
console.log(arguments.callee);//->函數本身
}
sum(); //Uncaught TypeError:sum is not a function
fn();
```
### 不管條件是否成立,都要進行變量提升
不管條件是否成立,**判斷體**當中出現的`var`或則`function`都會進行變量提升,但是在最新版本的瀏覽器當中,function聲明的變量只能提前聲明不能定義了。(前提:函數是在判斷體中)
老版本將瀏覽器的function是聲明加定義,新版本只會聲明(說的是放置在判斷體中時的情況)
```
console.log(num); //->undefined
console.log(fn); //->undefined
if(1 !== 1){ //不會走
console.log(num);
console.log(fn);
var num = 12;
function fn(){}
console.log(num);
console.log(fn);
}
console.log(fn); //=>undefined
```
```
console.log(num); //->undefined
console.log(fn); //->undefined
if(1 === 1){
console.log(num); //->undefined
console.log(fn); //->函數體本身
var num = 12;
function fn(){}
console.log(num); //->12
console.log(fn); //=>函數體本身
}
console.log(fn); //=>函數體本身
```
```
console.log(fn); //undefined
for(var i=0;i<9;++i){
function fn(){}
}
```
> 代碼執行到條件判斷的地方
>[條件不成立]
>進入不到判斷體中,此時之前聲明的變量或則函數依然是undefined
>
>[條件成立]
>進入條件判斷體中的第一件事情不是代碼執行,而是把之前變量提升沒有定義的函數首先定義了(進入到判斷體中函數就定義了,為了迎合ES6中的塊級作用域)
```
f = function(){
return true;
}
g = function(){
return false;
}
~function(){
//=>[私有作用域]
//變量提升:g
//雖然g提升了但在新版本中,因為這貨在判斷體里,只提升不會定義
//故g()報錯
if(g()&&[]==![]){
f=function(){
return false;
};
function g(){
return true;
}
}
}();
console.log(f()); //
console.log(g()); //false
```
### 重命處理
```
fn(); //4
function fn(){
console.log(1);
}
fn(); //4
function fn(){
console.log(2);
}
fn(); //4
var fn = 13;
fn(); //報錯
function fn(){
console.log(3);
}
fn();
function fn(){
console.log(4);
}
fn();
```
在變量提升階段,如果名字重復了,不會重新的進行聲明,但是會重新進行定義(后面賦的值會把前面賦的值替換掉)
## 作用域鏈
```
function fn(){
a = 123; // <=>window.a = 123;
console.log(a); //=>123
}
fn();
console.log(a); //=>123
```
>函數執行形成一個私有的作用域(保護私有變量),進入到私有作用域中,首先變量提升(聲明過的變量是私有的),接下來代碼執行
>1. 執行的時候遇到一個變量,如果這個變量是私有的,變量不受外界干擾
>2. 如果當前這個變量不是私有的,我們需要向它的上級作用域進行查找,上級也沒有,則繼續向上查找,直到找到window全局作用域為止,我們把這種查找機制叫做**作用域鏈**
>1)如果上級作用域有,我們當前操作的都是上級作用域中的變量(假如我們在當前作用域把值改了,相當于把上級作用域中的這個值給改了)
>2)如果上級作用域中沒有這個變量(找到window也沒有)
>變量 = 值:相當于給window設置了一個屬性,以后再操作window下就有了
>alert(變量):想要輸出這個變量,但此時是沒有的,所以會報錯
```
console.log(x,y);//undefined undefined
var x = 10,
y = 20;
//var x=10;var y=20;
function fn(){
console.log(x,y); //undefined 20
var x = y = 100;
//var x = 100;y=100;
console.log(x,y);// 100 100
}
fn();
console.log(x,y);//10,100
```
作用域(開辟)->變量提升->逐行運行
- 空白目錄
- 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
- 方法重寫和方法重載
- 移動端
- 響應式布局開發基礎
- 項目一:創意簡歷