what 閉包?
- 紅寶書(p178)上對于閉包的定義:閉包是指有權訪問另外一個函數(shù)作用域中的變量的函數(shù)
- MDN 對閉包的定義:閉包是指那些能夠訪問自由變量的函數(shù)。
(其中自由變量,指在函數(shù)中使用的,但既不是函數(shù)參數(shù)arguments也不是函數(shù)的局部變量的變量,其實就是另外一個函數(shù)作用域中的變量。)
閉包產生的原因?
首先要明白作用域鏈的概念,其實很簡單,在ES5中只存在兩種作用域————全局作用域和函數(shù)作用域,當訪問一個變量時,解釋器會首先在當前作用域查找標示符,如果沒有找到,就去父作用域找,直到找到該變量的標示符或者不在父作用域中,這就是作用域鏈,值得注意的是,每一個子函數(shù)都會拷貝上級的作用域,形成一個作用域的鏈條
閉包有哪些表現(xiàn)形式?
- 返回一個函數(shù)
- 作為函數(shù)參數(shù)傳遞
使用場景
① 返回值
function fn(){
var name="lijq";
return function(){
return name;
}
}
var fncopy = fn();
console.log(fncopy()) //lijq
② 函數(shù)賦值
var fn2;
function fn(){
var name="lijq";
//將函數(shù)賦值給fn2
fn2 = function(){
return name;
}
}
fn() //要先執(zhí)行進行賦值,
console.log(fn2()) //執(zhí)行輸出lijq
③ 函數(shù)參數(shù)
function fn(){
var name="lijq";
return function callback(){
return name;
}
}
var fn1 = fn() //執(zhí)行函數(shù)將返回值(callback函數(shù))賦值給fn1,
function fn2(f){
//將函數(shù)作為參數(shù)傳入
console.log(f());//執(zhí)行函數(shù),并輸出
}
fn2(fn1)//執(zhí)行輸出lijq
④ IIFE
(function(){
var name="hello";
var fn1= function(){
return name;
}
//直接在自執(zhí)行函數(shù)里面調用fn2,將fn1作為參數(shù)傳入
fn2(fn1);
})()
function fn2(f){
//將函數(shù)作為參數(shù)傳入
console.log(f());//執(zhí)行函數(shù),并輸出
}
⑤ 循環(huán)賦值
//每秒執(zhí)行1次,分別輸出1-10
for(var i=1;i<=10;i++){
// IIFE
(function(j){
//j來接收
setTimeout(function(){
console.log(j);
},j*1000);
})(i) //i作為實參傳入
}
// 也可以使用let 替換var
⑥ getter/setter
function fn(){
var name='lijq'
setName=function(val){
name = val;
}
getName=function(){
return name;
}
//將setName,getName作為對象的屬性返回
return {
setName,
getName
}
}
var fn1 = fn();//返回對象,屬性setName和getName是兩個函數(shù)
console.log(fn1.getName());//lijq
fn1.setName('lijq-change');//setter修改閉包里面的name
console.log(fn1.getName());//lijq-change
⑦ 緩存cache
// 比如求和操作,如果沒有緩存,每次調用都要重復計算,采用緩存已經(jīng)執(zhí)行過的去查找,查找到了就直接返回,不需要重新計算
var fn=(function(){
var cache={};//緩存對象
var calc = function(arr){//計算函數(shù)
var sum=0;
//求和
for(var i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum;
}
return function(){
var args = Array.prototype.slice.call(arguments,0);//arguments轉換成數(shù)組
var key=args.join(",");//將args用逗號連接成字符串
var result , tSum = cache[key];
if(tSum){//如果緩存有
console.log('從緩存中?。?,cache)//打印方便查看
result = tSum;
} else {
//重新計算,并存入緩存同時賦值給result
result = cache[key]=calc(args);
console.log('存入緩存:',cache)//打印方便查看
}
return result;
}
})();
fn(1,2,3);
fn(1,2,3);
fn(1,2,3,4,5);
fn(1,2,3,4,5,8);
⑧ 迭代器
var arr =['a1', 'b2', 123];
function incre(arr){
var i=0;
return function(){
//這個函數(shù)每次被執(zhí)行都返回數(shù)組arr中 i下標對應的元素
return arr[i++] || 'done';
}
}
var next = incre(arr);
console.log(next());//a1
console.log(next());//b2
console.log(next());//123
console.log(next());//done