定義:閉包 當一個函數的返回值是另外一個函數,而返回的那個函數如果調用了其父函數內部的變量,且返回的這個函數在外部被執行 就產生了閉包.閉包是一個環境,具體指的就是外部函數–高階函數。
說白了就是一個環境,能夠讀取其他函數內部的變量。
本質上,閉包是將函數內部和函數外部連接起來的橋樑。
用處:1.讀取函數內部的變量;
2.這些變量的值始終保持在內存中,不會在外層函數調用後被自動清除。
優點:1:變量長期駐紮在內存中;
2:避免全局變量的污染;
3:私有成員的存在 ;
特性:1:函數套函數;
2:內部函數可以直接使用外部函數的局部變量或參數;
3:變量或參數不會被垃圾回收機制回收 GC;
缺點:
常駐內存 會增大內存的使用量 使用不當會造成內存泄露,詳解:
(1)由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
(2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
var counter = 10;
function add () {
var counter = 0;
return function () {
counter+=1;
alert(counter)
}
}
// var s = add();
// s()// 1
// s()// 2
// s()// 3
function fn () {
var num = 223;
var fn1 = function () {
console.log(num)
}
num++;
return fn1;
}
// var fn2 = fn()
// fn2()//224
// fn2()//224
// fn2()//224
// 用途:閉包解決索引值問題
oLi = document.getElementsByTagName("li");
for (var i = 0; i < oLi.length; i++) {
/*oLi[i].onclick = function (index) {
return function () {
console.log(index)//
}
}(i)*/
(
function (j) {
oLi[j].onclick = function () {
console.log(j)
}
}
)(i)
}
// 私有成員的存在
var aaa = (function () {
var a =1 ;
function bbb () {
a++;
console.log(a)
};
function ccc () {
a++;
console.log(a)
};
return {//json結構
b:bbb,
c:ccc
}
})()
// aaa.b()//2
// aaa.c()//3
// aaa.b()//4
// aaa.c()//5
// function aaa(a){
// var b = 5;
// function bbb(){
// a++;
// b++;
//// debugger 1:斷點調試 2:打斷點
// alert(a);
// alert(b);
// }
// return bbb;
// }
//
// var ccc = aaa(2);
//
// ccc();
// ccc();
// var count = (function(){
// var a = 0;
// function add(){
// a++;
// return a;
// }
//
// return add;
//
// })()
//
// count();
// count();
//
// var nowcount = count();
//
// alert(nowcount);
// 在實際開發中,閉包主要是用來封裝變量,收斂權限 變量的管理方案
function isFirstLoad(){
var list=[];
return function(option){
if(list.indexOf(option)>=0){ //檢測是否存在於現有數組中,有則說明已存在
console.log('已存在')
}else{
list.push(option);
console.log('首次傳入'); //沒有則返回true,並把這次的數據錄入進去
}
console.log(list)
}
}
// var ifl=isFirstLoad();
// ifl("zhangsan");
// ifl("lisi");
// ifl("zhangsan");
// 外界想訪問list變量,只能通過我定義的函數isFirstLoad來進行訪問,
// 想訪問list的外界只提供了isFirstLoad這一個接口。至於怎麼操作list,
// 已經定義好了,外界能做的就只是使用函數,然後傳幾個不同的參數
var val=function(){
var that=this;
var variable={};
variable.varity=1;
var returnVal={};
this.isString=function(str){
try {
if (typeof str !== "string") {
throw "TypeErr";
}else{
return true;
}
} catch (e) {
if (e == "TypeErr") {
return false;
}
}
}
//讀
returnVal.getter=function(str){
var isStr=that.isString(str);
if(isStr){
return variable[str];
}else{
console.error("input type must string!!!!!");
}
}
//寫
returnVal.setter=function(key,value){
var isStr=that.isString(key);
if(isStr){
if(variable[key]==undefined){
eval(variable[key]);
}
variable[key]=value;
}else{
console.error("input type must string!!!!!");
}
}
return returnVal;
}
var val= val();//初始化方法
console.log(val.getter("varity"));// 1
val.setter("va222rity",3);//不存在重新添加並賦值
console.log(val.getter("va222rity")); // 3
// 閉包的一個實際的應用 不會發生誤操作(讀寫已經分離)