九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
我也來說說啥是閉包,本來很簡單的事,別人怎么都講那么復(fù)雜
先說句題外話,如果你覺得我講的明白的話給個(gè)反饋,點(diǎn)個(gè)贊,留個(gè)言啥的。
為啥js中有閉包這個(gè)東西,其他后臺語言里沒有?
當(dāng)一個(gè)函數(shù)調(diào)用時(shí),內(nèi)部變量的查找會(huì)按作用域鏈條來查找,按理說不會(huì)有啥特殊情況出現(xiàn),js之所以會(huì)出現(xiàn)閉包這個(gè)現(xiàn)象,原因就是你調(diào)用的這個(gè)函數(shù)是另外一個(gè)函數(shù)的返回值。
說到函數(shù)能作為返回值,這是跟js中函數(shù)類型是第一類對象這種語言設(shè)計(jì)方式有關(guān),我會(huì)先介紹第一類對象對js的編碼風(fēng)格的影響。當(dāng)然你直接可以看第二部分,關(guān)于閉包的說明。
第一部分
啥是第一類對象呢,我?guī)湍惆俣攘恕?/span>
第一類對象不一定是面向?qū)ο蟪绦蛟O(shè)計(jì)所指的物件,而可以指任何程序中的實(shí)體。一般第一類對象所特有的特性為:
可以被存入變量或其他結(jié)構(gòu)
可以被作為參數(shù)傳遞給其他函數(shù)
可以被作為函數(shù)的返回值
可以在執(zhí)行期創(chuàng)造,而無需完全在設(shè)計(jì)期全部寫出
即使沒有被系結(jié)至某一名稱,也可以存在

在js中object類型就是第一類對象。你能怎么使用object類型。就怎么能使用function類型。
這里先說一下js中函數(shù)的四大作用
1.可以調(diào)用執(zhí)行。程序世界里,函數(shù)的基本作用就是可復(fù)用代碼段的封裝,可以直接調(diào)用。
2.可以按照對象的使用方式來使用。原因就是js中函數(shù)類型是第一類對象。準(zhǔn)確的來說js中函數(shù)本身就是對象。對象能做啥,他當(dāng)然也能做。
3.可以提供作用域。js中沒有塊級作用域的概念。函數(shù)是提供作用域的最小單位。
4.可以作為構(gòu)造函數(shù)。這一作用算是函數(shù)的特殊作用??梢宰饔蒙善渌麑ο蟮哪0?。也就是可以通過函數(shù)來模擬類的實(shí)現(xiàn)。

在講閉包之前,先大致對函數(shù)的使用方式與對象和數(shù)組(數(shù)組本來就是對象,當(dāng)然函數(shù)也是)做個(gè)對比。
1.關(guān)于字面量
對象的字面量"{}"
  1. var a = new Object();
  2. a.b = "xxx";
  3. //等同于
  4. var a = {};
  5. a.b = "xxx";
  6. //或者
  7. var a = {
  8.         b : "xxx"
  9. };

復(fù)制代碼
數(shù)組的字面量"[]"
  1. var a = new Array('a','b','c');
  2. //等同于
  3. var a = ['a','b','c'];
復(fù)制代碼

那么函數(shù)呢,我們平常聲明函數(shù)的方式,可以理解成是一種字面量(我沒有說是)
  1. function a(x,y){
  2.         return x + y;
  3. }
  4. //相當(dāng)于如下對象的字面量
  5. var a = new Function(x,y,"return x + y;");
復(fù)制代碼

注意:上面所有a 都是對象的引用
2.關(guān)于匿名函數(shù)
同樣也有匿名對象和匿名數(shù)組,我們先看看他們是怎么使用的
  1. var b = ({
  2.         a : "xxx"
  3. }).a
  4. alert(b) // "xxx"

  5. for(var i = 0; i < [1,2,3].length; i++){
  6.         console.log(i);
  7. }
復(fù)制代碼

同樣函數(shù)也有匿名的
  1. funcion(){
  2.         alert("11");
  3. } 
  4. //因?yàn)楹瘮?shù)的最基本功能是調(diào)用,匿名函數(shù)也可以調(diào)用(我習(xí)慣稱呼為函數(shù)自執(zhí)行,一般書上都叫函數(shù)立即調(diào)用表達(dá)式)
  5. (function(){alert(11)})();
復(fù)制代碼

3.可以存進(jìn)變量或者其他結(jié)構(gòu)。
因?yàn)閿?shù)組元素中可以存入數(shù)組,當(dāng)然也可以存入函數(shù)。對象也是,鍵值對的值可以存入任何東西,當(dāng)然也可以存入函數(shù),這時(shí)我們一般都用匿名函數(shù),例如
  1. var a = function(){};//這種聲明函數(shù)的方式也叫函數(shù)直接量。
  2. var a = {
  3.         say : function(){//....}
  4. };
復(fù)制代碼

4.可以做為參數(shù),傳入函數(shù)也就是平常我們說的回調(diào)函數(shù)。
眾所周知函數(shù)有參數(shù)和返回值
對象和數(shù)組作為參數(shù)沒得說,寫下函數(shù)相關(guān)的例子
  1. var a = function(b){
  2.         b();
  3. };
  4. // 可以傳入匿名函數(shù),jquery中各種回調(diào)都是匿名的
  5. a(function(){alert("123");});
  6. //傳入有名字的函數(shù),跟c聲明的位置無關(guān),這里涉及到變量提升的問題以及函數(shù)優(yōu)先初始化的問題。
  7. a(c);
  8. function c(){
  9.         alert("222")
  10. }
復(fù)制代碼

廣義的講,當(dāng)然了,回調(diào)函數(shù),傳入?yún)?shù)不一定非得函數(shù)變量,但是一定要包含函數(shù)的結(jié)構(gòu)(例如數(shù)組、object對象、自定義對象),如下
  1. var a = function(object)
  2.         object.say();
  3. }
  4. a({x :"2222",say :function(){alert("xxxx")}});
復(fù)制代碼


5.作為返回值
對象和數(shù)組作為函數(shù)的返回值沒得說,寫下函數(shù)相關(guān)的例子
  1. function a(){
  2.         return function(){
  3.                 alert("22222");
  4.         };
  5. }
  6. (a())();//alert "22222";
復(fù)制代碼




第二部分
現(xiàn)在還是說說為啥出了個(gè)閉包這個(gè)東西,原因就是你調(diào)用的那個(gè)函數(shù)是另一個(gè)函數(shù)的返回值,當(dāng)外部調(diào)用時(shí),會(huì)沿著這個(gè)返回值的函數(shù)作用域鏈條來找其內(nèi)部相關(guān)變量的。
先大致說下作用域鏈條的問題。
函數(shù)中識別變量,是一層層向外找的,首先在函數(shù)內(nèi)部找,看看是不是內(nèi)部聲明的,然后再到上一層找,沒找到,再往上,直到全局作用域。如果全局頁面都沒聲明,那瀏覽器就報(bào)錯(cuò)了。這一層層中的層是什么東西呢,就是函數(shù),因?yàn)楹瘮?shù)提供最小的作用域??磦€(gè)例子
  1. var a = 3;
  2. var b = 4;
  3. function outer(){
  4.         var a = 5;
  5.         var c = 7;
  6.         var d = 8;
  7.         console.log(a);//5,outer內(nèi)部的
  8.         console.log(b);//4,全局的
  9.         var inner = function(){
  10.                 var c  = 6;
  11.                 console.log(b);//4,全局的
  12.                 console.log(c);//6,inner內(nèi)部的
  13.                 console.log(d);//8,outer內(nèi)部的
  14.                 //console.log(e); //報(bào)錯(cuò),沒找到
  15.                 b = 0 //找到全局的
  16.                 d = "xxx";
  17.         }
  18.         inner();
  19.         console.log(b);//0,找全局的b
  20.         console.log(d);//"xxx", outer內(nèi)部的
  21. }
  22. outer();
復(fù)制代碼

作用域鏈條我們明白了,然后咱再來看看閉包的情形
  1. //代碼1
  2. function a(){
  3.         var x = 0;
  4.         return function(){
  5.                 x++;//此函數(shù)的作用域鏈能看到x
  6.                 console.log(x);
  7.         }
  8. }

  9. var fun = a();//a返回的是個(gè)函數(shù),保存起來沒問題。
  10. fun()//打印1
  11. fun()//打印2
復(fù)制代碼

為啥打印2而不是1呢,原因是
因?yàn)閍中返回個(gè)函數(shù),我們要調(diào)用這個(gè)函數(shù),瀏覽器一看,你要運(yùn)行的是函數(shù),函數(shù)是有作用域鏈條的,哦,x我能找到,保證不報(bào)錯(cuò)的。里面的x當(dāng)然也能自增加了
說的直白點(diǎn)就像如下代碼一樣
  1. //代碼2
  2. var x = 0;
  3. var fun = function(){
  4.         x++;
  5.         console.log(x);
  6. }
  7. fun();//打印1
  8. fun();//打印2
復(fù)制代碼

補(bǔ)充:經(jīng)網(wǎng)友提醒,閉包有占用內(nèi)存的問題,這里說下,因?yàn)榇a1中fun是一個(gè)函數(shù)的引用,瀏覽器對應(yīng)的會(huì)對其作用域鏈條中的變量x做了保存,因而會(huì)占用內(nèi)存。達(dá)到的效果就跟代碼2中的x一樣。
要釋放其內(nèi)存可以把其引用置空,使a返回的那個(gè)匿名函數(shù)無引用指向它,自然垃圾回收器會(huì)回收的。代碼如下
  1. //代碼3
  2. function a(){
  3.         var x = 0;
  4.         return function(){
  5.                 x++;//此函數(shù)的作用域鏈能看到x
  6.                 console.log(x);
  7.         }
  8. }

  9. var fun = a();//a返回的是個(gè)函數(shù),保存起來沒問題。
  10. fun()//打印1
  11. fun()//打印2
  12. //以后不再使用了,注意要釋放內(nèi)存
  13. fun = null;

復(fù)制代碼

注意:
如果我換種調(diào)用方法呢
(a())();//打印1
(a())();//打印1
誒,為啥第二次不打印2了呢。原因很簡單,因?yàn)閮纱握{(diào)用返回的不是同一個(gè)函數(shù)引用,因此是兩條作用域鏈條。

說的直白點(diǎn)就像如下的代碼
  1. var x1 = 0;
  2. (function(){
  3.         x1++;
  4.         console.log(x1);
  5. })();//打印1
  6. var x2 = 0;
  7. (function(){
  8.         x2++;
  9.         console.log(x2);
  10. })();//打印1
復(fù)制代碼

這種使用方式,就不會(huì)有出現(xiàn)閉包常駐內(nèi)存的情況,因?yàn)槊看问褂枚寄涿?,?dāng)然了,也失去了閉包的意義。

大體閉包這種現(xiàn)象我是解釋明白了。我沒有給閉包下明確的定義,不同的書有不同的說法。
有的說,返回的那個(gè)函數(shù)是閉包,有的說返回的函數(shù)提供的作用域鏈條是閉包。有的甚至把其得到效果說是閉包,
大體是這么說的,通過這種方式,能訪問某個(gè)部函數(shù)內(nèi)部的私有變量,這種方式稱為閉包。
不管怎么說都是跟函數(shù)的作用域鏈條相關(guān)的。更有甚者也有說所有函數(shù)都是閉包。
我個(gè)人覺得會(huì)出現(xiàn)閉包這個(gè)東西,主要原因就是跟js中函數(shù)是第一類對象有關(guān),因?yàn)槟阏{(diào)用的一個(gè)函數(shù)可能不是直接聲明的,而是其他函數(shù)直接return的函數(shù)或者return某種結(jié)構(gòu)中的一個(gè)函數(shù)。
關(guān)于是返回某種結(jié)構(gòu)的中函數(shù),舉例如下
  1. function a(){
  2.         var x = 0;
  3.         var y = {name :"張三"};
  4.         var f1 = function(){
  5.                 x ++;
  6.         }
  7.         var f2 = function(name){
  8.                 y.name =         name;
  9.         }
  10.         return [f1,f2];
  11. }
  12. var b= a()
  13. b[0]();
  14. b[0]("李四");
復(fù)制代碼

再寫個(gè)
  1. function a(){
  2.         var name = null;
  3.         var f1 = function(n){
  4.                 name = n;
  5.         };
  6.         var f2 = function(){
  7.                 return name;
  8.         };
  9.         return {
  10.                 setName : f1,
  11.                 getName : f2
  12.         }
  13. }
  14. var o =a();
  15. o.setName("老姚");
  16. var myName = o.getName();
  17. console.log(myName);
復(fù)制代碼

如果在講上述例子改寫新的形式,把函數(shù)改成匿名的(有的人甚至覺得匿名函數(shù)是閉包,那樣我會(huì)說,看來所有函數(shù)都是閉包了),就是一種設(shè)計(jì)模式:模塊模式。
  1. var person = (function(){
  2.         var name = null;
  3.         var f1 = function(n){
  4.                 name = n;
  5.         };
  6.         var f2 = function(){
  7.                 return name;
  8.         };
  9.         return {
  10.                 setName : f1,
  11.                 getName : f2
  12.         }
  13. }
  14. )();
  15. person.setName("老姚");
  16. console.log(person.getName());
復(fù)制代碼

由此可以看出來應(yīng)用閉包不只是簡單的寫個(gè)計(jì)數(shù)器啥的。
第三部分
最后再來看看,如何避免閉包。
有時(shí)我們本意不想用閉包的,
如下,我想彈出0,1,2的,結(jié)果都會(huì)彈出3.
  1. var fun = function(){
  2.         var a = [];
  3.         for(var i = 0;i<3;i++){
  4.                 a.push(function(){
  5.                         return i;
  6.                 })
  7.         }
  8.         //console.log(i);//因?yàn)閖s中沒有塊級作用域,i最后變成3,而不是報(bào)錯(cuò)
  9.         return a;
  10. }
  11. var a = fun();
  12. alert(a[0]());//3
  13. alert(a[1]());//3
  14. alert(a[2]());//3
復(fù)制代碼

可以改成
  1. var fun = function(){
  2.         var a = [];
  3.         for(var i = 0;i<3;i++){
  4.                 a.push(function(j){
  5.                         return function(){
  6.                                 return j;
  7.                         };
  8.                 }(i))
  9.         }
  10.         return a;
  11. }
  12. var a = fun();
  13. alert(a[0]())//0
  14. alert(a[1]())//1
  15. alert(a[2]())//2
復(fù)制代碼

最開始的那個(gè)例子也可以避免閉包,改成
  1. function a(){
  2.         var x = 0;
  3.         return function(x){
  4.                 return function(){
  5.                         x++;//此函數(shù)的作用域鏈能看到x
  6.                         console.log(x);
  7.                 }
  8.         }(x); 
  9. }
復(fù)制代碼

還有一種情況也會(huì)出現(xiàn)閉包現(xiàn)象,把內(nèi)部函數(shù)綁定了dom節(jié)點(diǎn)某種操作(onclick)的回調(diào)函數(shù),沒有寫在return語句里。道理是一樣的。寫在return里,是return后調(diào)用,綁定到dom上,比如說觸發(fā)點(diǎn)擊事件后再調(diào)用,其道理是一樣的,作用域鏈條該怎么找就怎么找。

最后再說一句,閉包最起碼的應(yīng)用,就是我們可以把一些全局變量封裝起來,通過這種方式來不污染全局,例如上面的模塊模式例子。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
JavaScript 學(xué)習(xí)-9.使用let聲明變量
JavaScript(六)-函數(shù)
Javascript 嚴(yán)格模式詳解
JavaScript中var、let、const的區(qū)別 (詳解)
五句話搞定JavaScript作用域
JavaScript變量和函數(shù)的預(yù)解析
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服