国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

零基礎輕松學JavaScript閉包

2019-11-19 18:11:23
字體:
來源:轉載
供稿:網友

本文面向初學者,大神輕噴。

閉包是什么?

初學javascript的人,都會接觸到一個東西叫做閉包,聽起來感覺很高大上的。網上也有各種五花八門的解釋,其實我個人感覺,沒必要用太理論化的觀念來看待閉包。

事實上,你每天都在用閉包,只是你不知道罷了。

比如:

var cheese = '奶酪';var test = function(){  alert(cheese);}

OK,你已經寫了一個閉包。

函數也是一個數據類型

變量 cheese 是在全局作用域中的一個變量,當你創建了一個 test 函數,那么,test 和 cheese 就共享一個全局作用域。

你要額外明白的一點是,在js中,函數和變量本質上是一個東西。函數也是一個數據類型。

從上面的定義中也能看出來這一點。你要是不相信的話,我們來看一下咯。

alert(cheese);alert(test);

 

 

讓我們再來看看 test 和 cheese各是什么類型:

alert(typeof test);

 

alert(typeof cheese);

 

看到了吧,只是類型不同而已,他們都是數據類型。

唯一的不同點就是,函數類型的 test 可以擁有自己內部邏輯,而string類型的 cheese 只能存放一個字面值,這就是區別,僅此而已。

一目了然了,唯一不同的就是普通變量是字面值一樣的存在,而函數需要打個括號才能執行而已。

你看,我現在打一個括號:

test();

打了括號,才會執行函數里面的邏輯。

作用域

讓我們回到閉包,現在將之前的代碼做一個小小的變動:

var cheese = '奶酪';var test = function(){  alert(cheese);}function test2(){  var cheese = null;  test();}test2();

那么,你覺得現在 alert 出來的是 null 還是奶酪呢?

思考一下。。。

對的,彈出來的還是奶酪。

之前已經說過了,函數 test 和 變量 cheese 同處于一片藍天下 -- 同一個作用域。

函數 test 和 變量 cheese 共同享有的作用域叫做全局作用域,就好像地球一樣,我們所有的人都享有這個地球,能夠在這里呼吸,吃飯,玩耍。

對test而言,他能訪問到的作用域只有它本身的閉包和全局作用域:

也就是說,正常情況下他訪問不到其他閉包里的內容,在 test2 里面定義的變量跟它沒有半毛錢關系,所以彈出來的 cheese 依舊是全局作用域里的 cheese。

函數可以創造自己的作用域。

我們剛才定義了一個 test 函數,{ } 包裹起來的部分就形成了一個新的作用域,也就是所謂的閉包。

其實你深刻了解了作用域的原理后,閉包也就理解了。

就好比地球是一個全局作用域,你自己家的房子是一個函數,你的房子是私人空間,就是一個局部作用域,也就是你自己建了一個閉包!

你透過窗戶可以看見外邊的景色,比如院子里的一棵芭蕉樹,你于是通過眼鏡觀察看到了芭蕉樹的顏色,高度,枝干的粗細等等。

這一棵芭蕉樹相當于一個全局變量,你在自己的閉包內可以訪問到它的數據。

所以,在這個例子中,test 就是一個房子,在里面可以通過窗戶訪問到全局作用域中的奶酪 ―― 變量 cheese。

也就是說,cheese 在被 test 訪問到的時候,就進入了它的閉包。

這樣解釋,你是否覺得好理解一點呢?

現在你是否可以理解一開始我說,閉包這東西其實我們天天都在用的意思了呢?

我們給出閉包的第一個注解:

1. 閉包就是在函數被創建的時候,存在的一個私有作用域,并且能夠訪問所有的父級作用域。

回到剛才的例子:

var cheese = '奶酪';var test = function(){  alert(cheese);}function test2(){  var cheese = null;  test();}

在這個例子中,test 和 test2 各自享有一個作用域,對不對?而且他們互相不能訪問。比如,我在 test 中定義的一個變量,test2就無法直接訪問。

var test = function(){  var i = 10;}function test2(){  alert(i);}test2();

像這樣,一旦執行 test2 函數,編譯就不通過,因為在 test2的閉包內,根本找不到變量 i 。它首先會在自己的閉包內尋找 i,找不到的話就去父級作用域里找,這邊的父級就是全局作用域,很遺憾,還是沒有。這就是所謂的作用域鏈,它會一級一級往上找。如果找到最頂層,還是找不到的話,就會報錯了。

在這里,還有一個需要注意的點就是:如果某一個閉包中對全局作用域(或父級作用域)中的變量進行了修改,那么任何引用該變量的閉包都會受到牽連。

這的確是一個需要注意的地方。

舉個例子

var cheese = '奶酪';var test = function(){  cheese = '奶酪被偷吃了!'}function test2(){  alert(cheese);}test();test2();

結果是:

很有趣,是不是呢?

當我們在定義一個函數,就產生了一個閉包,如果這個函數里面又有若干的內部函數,就是閉包嵌套著閉包。

像這樣:

function house(){  var footBall = '足球';  /* 客廳 */  function livingRoom(){    var table = '餐桌';    var sofa = '沙發';    alert(footBall);  }    /* 臥室 */  function bedRoom(){    var bed = '大床';  }    livingRoom();}house(); 

函數house是一個閉包,里面又定義了兩個函數,分別是livingRoom客廳,和bedRoom臥室,它們各自形成一個自己的閉包。對它們而言,父級作用域就是house。

如果我們希望在客廳里踢足球,在livingRoom函數執行的時候,它會先在自己的閉包中找足球,如果沒找到,就去house里面找。一層一層往上找,直至找到了為止。當然,這個例子可能不是很恰當。但起碼展示了作用域,閉包之間的聯系。

再說明一下, 閉包就是在函數被創建的時候,存在的一個私有作用域,并且能夠訪問所有的父級作用域。因此,從理論上講,任何函數都是一個閉包!

2. 如何將私有數據暴露出去

之前有這樣一個例子

var test = function(){  var i = 10;}function test2(){  alert(i);}test2();

函數 test 和 test2 各自形成一個閉包,兩個閉包之間無法訪問對方的私有數據。比如,在 test 中定義的變量,在 test2 里面是無法直接訪問到的。

那么問題來了, 當然,這邊和挖掘機沒關系。這里的問題是,有沒有什么辦法讓 test2 可以訪問到其他閉包中的私有變量呢?

辦法當然是有的,最直接的想法就是,大不了我定義一個全局變量,在 test 中將私有數據賦給全局變量,然后在 test2 里面就能訪問到了。

是的,因為兩個函數共同享有一個全局作用域,所以這個辦法確實可行。我在很多項目里也的確看到很多人就是這么做的。

那么,有沒有一種更好的方法呢?要知道,全局作用域是一個比較敏感的地方,一不小心就會出現變量名重復的問題。順便說一句,在全局作用域中,盡量不要使用諸如 temp , a , b , c 這一類的大眾化變量。

于是,這就牽扯到返回值的相關知識了,你在C語言的教材中肯定見慣了類似于這樣的代碼

int sum(int a,int b){  return a + b;}int all = sum(3,5);

這是一個簡單的求和函數,很多人慢慢地養成了這樣一個觀念,就是函數的返回值就是一個字面值,要么是數字類型,要么是布爾類型,或者是字符串。

在很多強類型的語言,諸如 Java,C,C++, 確實如此。但是 return 在 JavaScript 中卻大有來頭。

在上一節已經說明了,js 的函數也是一種數據類型,你可以把函數看成是和int , float , double 一樣的東西。

那么,既然int可以當做函數的參數或者返回值,函數當然也可以!

請看下面兩句話:

在js中

如果函數被當做參數傳進去了,它就是所謂的回調函數。

如果函數被當做返回值return出去了,它就是把一個閉包return出去了。

這一章不講回調函數,如果你不清楚啥叫回調函數,可以去看看有關文章

還是上面的那個例子,我們希望在 test2 中可以訪問到 test 里面的變量,可以這樣做:

var test = function(){  var i = 10;  /* 定義一個函數將變量i暴露出去 */  var get = function(){    return i ;  }  return get; //將獲得i的函數暴露出去}function test2(){  var fn= test();//接收test暴露出來的函數  alert(fn()); //獲得test中的私有數據}test2();

test 函數中的 get 方法是一個內部函數,它自己也形成了一個閉包, test 是他的父級作用域,因此它可以獲取i的值。

i 進入 get 方法的閉包,被包了起來,然后最終被返回了出去。

而對于 test2 來說,是可以訪問到 test函數的,因此可以調用并執行 test 函數,從而獲取其返回值。

你可能會說,我直接在test中把i給return出去就好了嘛,干嘛這么麻煩。

是的,言之有道理。

可是,如果我要訪問 test 中多個私有數據咋辦捏?

這下你可明白了吧!

現在,我們給出關于閉包的第二個注解:

從應用的角度來看,閉包可以將函數或者對象的私有數據暴露出去,而不影響全局作用域。

通過這張圖,是不是好理解一些了呢?我們這一節單說函數里的私有數據。

2. 將私有數據包裝成json對象

剛才的例子說明,在js中,return出去的可以是基本數據類型,也可以是函數類型。

其實,JavaScript是一種基于對象的語言,也有對象的概念,所以,我們可以把你需要的東西包裹成一個對象返回出去!

上代碼:

var test = function(){  var apple = '蘋果';  var pear = '梨子';  /* 定義一個函數將水果暴露出去 */  var getFruit = {    apple : apple ,    pear : pear  }  return getFruit; //將獲得i的函數暴露出去}function test2(){  var getFruit = test();//接收test暴露出來的函數  console.log(getFruit);}test2();

像這樣用 { } 括起來的東西就是一個js對象,也就是所謂json。你可能經常會聽到json這個詞,覺得還挺高大上的。其實它就是一個用 { } 包起來的數據而已。

里面是鍵值對的形式,非常類似于Java里面的HashMap。

在這個例子中,我們可以直接把需要暴露的私有數據用一個 { } 包起來,構成一個json對象return出去就可以啦。

因為是 js 對象,alert 不能看到里面的具體內容,所以我們使用 console.log() ,結果如下:

展開后:

這樣是不是也可以了?多出來的 proto 是原型鏈,以后會講到。

3. 我們來做一個紫金葫蘆

大家都還記得西游記里孫悟空用遮天的把戲騙來的紫金葫蘆嗎,只要你拿著這個葫蘆,叫一聲別人的名字,如果答應了,別人就會被吸進去。

OK,這個紫金葫蘆里面不正如一個閉包嗎?

對不對嘛,所以,我們用閉包的知識來做一個好玩的東西吧。

<body>  <div id='box' style='width:50px;height:50px;background:#333;color:#fff;text-align:center;line-height:50px'>小妖</div></body>

紫金葫蘆里面的源碼大概是這樣的:

var 紫金葫蘆 = function(id){  var domElement = document.getElementById(id);  var returnObject = {    domElement : domElement ,    backgroundColor : function(color){      domElement.style.backgroundColor = color;    },    click : function(fn){      domElement.onclick = fn;    }  };  return returnObject;}

注:我純粹是為了看起來方便而采用中文定義變量,在實際開發中,千萬不要使用中文變量。

我們在返回出去的對象上加了三個東西:

1.domElement

你傳進來一個id,我就用 document.getElementById 來包一下,得到一個dom元素,最終要操作的也就是這個dom元素。也就是說:

var box1 = 紫金葫蘆('box').domElement;var box2 = document.getElementById('box');alert(box1 === box2);Paste_Image.png

他們是一個東西,一樣的。

紫金葫蘆('box');

這行代碼一旦執行,紫金葫蘆就會返回 returnObject 對象,也就是說。我們喊一聲 “box”,那個id為box的小妖一答應,就被裝進來了,然后我們可以對它為所欲為!

比如,給它換一個背景色:

2.backgroundColor 給元素添加背景色的方法

var box = 紫金葫蘆('box');box.backgroundColor('red');

3.click 給元素添加點擊事件,需要傳入一個回調函數

var box = 紫金葫蘆('box');box.click(function(){  alert('就沒人吐槽這個無聊的作者么,小妖也有尊嚴的好么,啊喂??!');});

結果:

也許你已經發現了,這些方法是不是和jQuery有點類似呢?

以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持武林網!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 通山县| 满城县| 湄潭县| 岑巩县| 丰原市| 科技| 神木县| 长海县| 安康市| 利辛县| 石家庄市| 太保市| 新干县| 庄浪县| 赞皇县| 榆树市| 宜阳县| 安宁市| 小金县| 文山县| 精河县| 兴海县| 师宗县| 嵊州市| 高雄县| 金坛市| 宿州市| 上虞市| 宜宾市| 普兰店市| 棋牌| 营口市| 醴陵市| 都兰县| 宕昌县| 大宁县| 谢通门县| 西平县| 怀宁县| 宁晋县| 盈江县|