目標:
* 如何定義和使用函數
* 如何向函數傳遞參數
* 了解預定義函數(內建函數)
* 了解變量作用域 ?
* “函數也是數據”
* 匿名函數的調用
* 回調函數
* 自調函數 ?
* 內嵌函數
* 以函數為返回值的函數 ?
* 能重定義自身的函數
* 閉包 ?
*****
# 函數
實質為代碼分組形式,便于重用;
組成:`function + 函數名 + 參數 + 函數體(代碼塊) + return (undefined)`
調用/請求:函數名 + ([參數])
傳遞的參數,少了,參數設為undefined,多了,忽略。
函數內建有**arguments**數組(類似數組的對象),可返回所接收的全部參數。
```
function args(){return arguments;}
args();
// Arguments?[callee: ?, Symbol(Symbol.iterator): ?]
args(1,2,3,4,true);
// Arguments(5)?[1, 2, 3, 4, true, callee: ?, Symbol(Symbol.iterator): ?]
```
# 內建函數
* parseInt()
* parseFloat()
* isNaN()
* isFinite()
* encodeURI()
* decodeURI()
* encodeURIComponent()
* decodeURIComponent()
* eval()
*****
## parseInt(a1,radix)
將a1轉換成整數類型,失敗返回NaN;
最好指明radix進制。
```
parseInt('abc123')
// NaN
parseInt('123abc')
// 123 在遇到第一個異常字符時會放棄轉換 ?
parseInt(NaN);
// NaN
parseInt(1e1)
// 10
parseInt('1e1');
// 1
```
`radix`一般默認為10,即10進制,例外情況:
首參數字符串是0x開頭,radix=16;
首參數字符串是0開頭,radix=8;
## parseFloat(a)
只支持轉換為10進制數,多余參數會忽略
可轉換指數數據,parseInt不支持 ?
```
parseFloat('123e2');
// 12300
parseInt('123e2', 10);
// 123
parseFloat('1e1');
// 10
```
## isNaN()
用于判斷輸入值是否是非數值,`true-非數,false-數值`,即返回false才可作算術運算
```
isNaN('a');
// true
isNaN('123');
// false
isNaN(parseInt(NaN));
// true
```
## isFinite()
檢查輸入是否一個既非infinity也非NaN數字,是否有限數值,true-有限
```
isFinite(Infinity);
// false
isFinite(12);
// true
```
## URI編碼與解碼
`encodeURI()`——域名部分不編碼
`encodeURIComponent()`——全部編碼
`decodeURI()`——域名部分不編碼
`decodeURIComponent()`——全部編碼
## eval()
欺騙函數,類似Function()
將傳入的string作為代碼執行,動態代碼,
* 性能差;
* 安全性差;
## alert()
# 變量的作用域 ?
全局變量:聲明在所有函數之外的變量;
局部變量:聲明在某個函數內部的變量,在該函數外是不可見的。
> 聲明一個變量如果沒有使用var語句,該變量會被默認為全局變量!
> 減少全局變量的數量
函數域優先于全局域
```
var a= 123;
function f() {
alert(a);
var a = 1; // 局部變量a聲明被提升至函數域頂部,只聲明被提升!
alert(a);
}
f(); // undefined 1
```
# 函數也是數據
函數標識記法,像變量一樣使用函數
函數名不能以數字開頭;
```
// function f(){return;}
var f = function(){return;}
typeof f; // "function"
```
函數特性:
* 它們所包含的是代碼;
* 它們是可執行的;
## 匿名函數
兩種用法:
* 將匿名函數作為參數傳遞給其他函數;
* 定義匿名函數來執行某些一次性任務;
## 回調函數 Callback()
將函數A傳遞給函數B,由B來執行A,A就是一個(匿名)回調函數
優勢:
* 匿名傳遞函數,節省全局變量;
* 將調用A的操作委托給B,節省代碼量;
* 提升性能;
## 自調函數 IIFE
`(function(參數){函數體})('入參')`
## 內部(私有)函數
好處:
* 確保全局名字空間的純潔性;
* 私有性,暴露該暴露的,封裝其余的;
## 返回函數的函數
`return function(){...};`
## 能重寫自己的函數
利用返回函數覆蓋舊函數;
執行一次性初始化工作;
* 外部重寫;
* 內部重寫;
```
function a(){
alert('A!');
a = function(){
alert('B!');
}
}
a(); // A!
a(); // B!
```
"瀏覽器特性探測"技術實現原理
# 閉包
## 作用域鏈
在函數中可訪問的變量,既可以來自自身作用域,也可以來自其父級作用域。這就是一條作用域鏈(scope chain)。
## 詞法作用域
每個函數在被**定義時**都會創建一個屬于自己的環境(作用域)
```
function f1(){var a = 1; f2();}
function f2(){return a;}
f1();
// Uncaught ReferenceError: a is not defined
```
我們可以在函數中對變量執行添加、移除和更新等操作,但函數只會看到該變量的最終狀態。因此我們可以重寫函數執行體,依舊能正常工作。
## 利用閉包突破作用域鏈
將函數移動到詞法作用域以外,函數仍記得其所定義作用域的路徑,保持著對原定義作用域的引用,依舊可以訪問原作用域的變量。
### 閉包1
```
function f(){
var b = 'b';
return function(){
return b;
}
}
b;
// Uncaught ReferenceError: b is not defined
var n = f(); // n 為全局變量,可以訪問f()私有空間
n;
// ? (){ return b; }
n();
// "b"
```
### 閉包2
```
var n;
function f(){
var b = "b";
n = function(){
return b;
}
}
f();
n();
// "b"
```
### 閉包3
函數綁定的是作用域本身,而非變量或返回值
```
function f(arg){
var n = function(){
return arg;
}
arg++;
return n;
}
var m = f(123);
m();
124
```
## Getter & Setter
通過匿名自調函數,供全局函數間接訪問局部變量secret
```
var getValue, setValue;
(function(){
var secret = 0;
getValue = function(){
return secret;
};
setValue = function(v){
secret = v;
};
})()
getValue(); // 0
setValue(123);
getValue(); // 123
```