1、什麼是閉包?
如圖所示,閉包就是一個定義在函數內部的函數,其作用是將函數內部和函數外部連接起來。
大家知道,作用域的問題,就是在函數內部定義的變量稱爲局部變量,外部取不到值。
下面我們通過代碼來更加詳細地看一下:
function A() {
let x = 1;
return function B() {
console.log(x);
x++;
}
}
console.log(A()); //直接打印返回的B
let b = A();
b(); //1
b(); //2
b(); //3
上述代碼我們可以看到的是再次執行A
函數的時候(其實這時候執行的是B
),會打印出1
,再次執行,會打印出2
,再執行會打印出3
。我們知道了閉包不僅可以拿到函數內部的變量,還可以保存內部的變量。
接下來,我們來看下閉包的使用場景,
for (var index = 0; index < 5; index++) {
setTimeout(function () {
console.log(index)
});
}
上面的代碼段,我們會打印5個5,爲啥呢?因爲當我們執行for循環時,setTimeout
是異步的,所以每次等for循環加完,再執行setTimeout
函數,一共執行5次。另一個原因因爲使用了var
聲明的。循環體裏的index
跟外部的index
是存在於同一個作用域,相當於在全局定義了一次。
下面我們來使用let
來聲明一下。結果就不一樣。分別打印0、1、2、3、4
,
for (let index = 0; index < 5; index++) {
setTimeout(function () {
console.log(index)
});
}
最後,我們使用閉包也來實現一下,同樣分別打印0、1、2、3、4
,在外部函數每次傳入實參時,也就是每次循環的值index
,作用到形參i
,因爲setTimeout
爲內部函數,每次都會記錄值,然後打印出來。
for (var index = 0; index < 5; index++) {
(function (i) {
setTimeout(function () {
console.log(i)
});
})(index);
}
另一個使用場景是柯里化
。下面一個問題我們將詳解。
2、什麼是柯里化?
是把接受多個參數的函數變換成接受一個單一參數(最初的第一個參數)的函數,並且返回接受餘下的參數而且返回結果的新函數的技術。
3、柯里化的好處?
- 提高適用性
- 延遲執行
- 提前確認
(1)、提高適用性
我們之前使用函數都是這樣寫,很冗餘。
function check(reg,text) {
return reg.test(text);
}
console.log(check(/\d+/g,'123456')) //true
console.log(check(/\d+/g,'abcd')) // false
console.log(check(/\d+/g,'r8r8r7')) // true
那麼我們通過柯里化的形式簡化一下。
function check(reg) {
return function (text) {
return reg.test(text);
}
}
const checkNum=check(/\d+/g);
console.log(checkNum('123456')) //true
console.log(checkNum('abcd')) // false
console.log(checkNum('r8r8r7')) // true
(2)、延遲執行
在需要的時候再執行。
const getResult = add(1)(2)(3);
// TODO
getResult();
或者
const getResult =add.bind(null,1).bind(null,2).bind(null,3);
// TODO
getResult();
(3)、提前確認
之前執行一次,就不會每次都去判斷了,下面舉例:
function getBindEvent(isIE) {
if(isIE){
return function () {
// TODO
console.log(1)
}
}
return function () {
// TODO
console.log(0)
}
}
const isIE = !!window.ActiveXObject||"ActiveXObject" in window;
const bindEvent = getBindEvent(isIE);
bindEvent();
最後這裏來實現下圖所示:
function add() {
// arguments是一個類數組
const args = Array.prototype.slice.call(arguments); // 將參數都放在一個數組裏
return function () {
// 下面的arguments與上面的不一樣,下面的arguments爲當前函數環境下的參數
if(arguments.length){ // 如果有傳進來的參數
args.push(...arguments); // 添加到數組裏
return arguments.callee; // 返回閉包函數自身
}
return args.reduce((n,m)=>{return n+m}); // 否則參數沒傳進來,就直接執行他
}
}
console.log(add(1)(2)()); // 3
console.log(add(1,2)(3)(4)()); // 10
本期結束,謝謝