整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          javascript中的閉包

          javascript中的閉包

          s中閉包closure,是指函數變量可以保存在函數作用域內,因此看起來是函數將變量“包裹”了起來,根據定義,包含變量的函數就是閉包。

          最初的定義

          閉包(closure),是指函數變量可以保存在函數作用域內,因此看起來是函數將變量“包裹”了起來。

          //根據定義,包含變量的函數就是閉包

          function foo() {

          var a=0;

          }

          cosole.log(a)

          // Uncaught ReferenceError: a is not defined



          《JavaScript高級程序設計》對閉包定義

          閉包是指有權訪問另一個函數作用域中的變量的函數


          //根據《JavaScript高級程序設計》,訪問上層函數的作用域的內層函數就是閉包

          function foo() {

          var a=2;

          function bar() {

          console.log(a);

          }

          bar();

          }

          foo();



          《JavaScript權威指南》對閉包定義

          函數對象可以通過作用域鏈相互關聯起來,函數體內部變量可以保存在函數作用域內,這就是閉包。



          var global ="global scope"; //全局變量

          function checkscope() {

          var scope="local scope"; //局部變量

          function f() {

          return scope; //在作用域中返回這個值

          };

          return f();

          }

          checkscope(); // 返回 "local scope"



          嚴格來說,閉包需要滿足三個條件:

          【1】訪問所在作用域;

          【2】函數嵌套;

          【3】在所在作用域外被調用
          有些人覺得只滿足條件1就可以,所以IIFE是閉包;有些人覺得滿足條件1和2才可以,所以被嵌套的函數才是閉包;有些人覺得3個條件都滿足才可以,所以在作用域以外的地方被調用的函數才是閉包

          為什么我們需要閉包,js閉包有什么用處

          首先來看一個例子,我們來實現一個計數器。

          var counter=0;

          function add() {

          return counter +=1;

          }

          add();

          add();

          add();// 計數器現在為 3

          現在我們已經達到了目的,可是問題來了,代碼中的任何一個函數都可以隨意改變counter的值,所以這個計數器并不完美。那我們把counter放在add函數里面不就好了么?

          function add() {

          var counter=0;

          return counter +=1;

          }

          add();

          add();

          add();// 本意是想輸出 3, 但輸出的都是 1

          所以這樣做的話,每次調用add函數,counter的值都要被初始化為0,還是達不到我們的目的。

          如何使用閉包

          所以這時候我們就要用閉包去解決這個問題了,先看代碼。


          var add=(function () {

          var counter=0;

          return function () {return counter +=1;}

          })();

          add();

          add();

          add();// 計數器為 3

          這時候我們完美實現了計數器。這段非常精簡,可以拆分成如下等價代碼。

          function outerFunction () {

          var counter=0;

          function innerFunction (){

          return counter +=1;

          }

          return innerFunction;

          }

          var add=outerFunction();

          add();

          add();

          add();// 計數器為 3

          這時候的add就形成了一個閉包。一個閉包由兩部分組成,函數和創建該函數的環境。環境是由環境中的局部變量組成的。對于閉包add來說,它由函數innerFunction和變量counter組成,所以這時候add是可以訪問變量counter的。

          使用閉包應注意的問題

          由于閉包會攜帶包含它的函數的作用域,因此會比其他函數占用更多的內存。因此可以手動解除對匿名函數的引用,以便釋放內存。

          function f2(){

          var n=22;

          var nAdd=function(){n++};

          return function(){

          return {

          n:n,

          nAdd:nAdd

          }

          }

          }

          //result2就是創建了一個匿名函數

          var result2=f2();

          //調用函數

          console.log(result2());

          result2().nAdd();

          console.log(result2());

          //解除對匿名函數的引用,以便釋放內存

          result2=null;

          么是閉包?相信很多人聽了有點懵,然后去百度查了一下官方的定義“閉包就是能夠讀取其他函數內部變量的函數”發現感覺自己更懵了。關于閉包如果只看那官方的定義的話確實不是讓人很容易理解。

          要想理解什么是閉包,首先得弄清楚什么是作用域,作用域可以理解為一個變量可以使用的范圍。

          在 JavaScript 中有兩種作用域類型:

          • 局部作用域

          在 JavaScript 函數中聲明的變量會成為函數的局部變量。局部變量的作用域是局部的所以只能在函數內部訪問它們。

          • 全局作用域

          函數外聲明的變量屬于全局變量。全局變量的作用域是全局的所以網頁的任意部分和函數都能夠訪問它。

          有了作用域做鋪墊,再來學習閉包就容易多了。

          function f1(){
              var n=10;//局部變量n
              					//在f1函數內部聲明的f2函數
              function f2(){
                alert(n);
              }
              return f2;//將f2函數作為f1函數的返回值
            }
            var fobj=f1();//f1調用完后的返回值是一個f2函數,此時fobj就是f2函數
            fobj(); // 輸出10,調用f2函數

          上述代碼就是一個簡單的閉包函數,閉包的用處有兩個,一個是可以讀取函數內部的變量,另一個就是在內存中讓這些變量的值始終保持。

            function f1(){
              var n=5;//局部變量n
              add=function(){  //fdd前面沒有使用var關鍵字,因此add是一個全局變量的匿名函數
                						n+=1
              					}
             					 //在f1函數內部聲明的f2函數
              function f2(){
                alert(n);
              }
              return f2;
            }
            var fadd=f1();
            result(); // 輸出5
            add();
            fadd(); // 輸出6

          從上面這段代碼中可以看出,fadd就是閉包f2函數。它一共運行了兩次,第一次的值是5,第二次的值是6。這證明函數f1中的局部變量n一直保存在內存中,并沒有在f1調用后被自動清除。因為f1是f2的父函數,而f2相當于賦給了f1一個全局變量,所以f2一直在內存中。而f2的存在又依賴于f1,因此f1也一直在內存中,不會在調用結束后回收。

          由于閉包會使函數中的變量都被保存在內存中,導致內存消耗很大,造成網頁的性能問題,在IE中還可能導致內存泄露,所以不能濫用閉包,在退出函數之前,將不使用的局部變量全部刪除。

          、詞法定義域 Lexical

          Closure閉包是編程語言Lexical Scoping的專有屬性,區別于dynamic scoping。即函數執行調用的是其在定義過程中的”變量定義域“,而非其在調用時候的變量定義域。

          Javascript的函數的初始狀態不僅包括函數本體而且包括函數定義過程所在的定義域。

          Like most modern programming languages, JavaScript uses lexical scoping. This means that functions are executed using the variable scope that was in effect when they were defined, not the variable scope that is in effect when they are invoked. In order to implement lexical scoping, the internal state of a JavaScript function object must include not only the code of the function but also a reference to the scope in which the function definition appears. This combination of a function object and a scope (a set of variable bindings) in which the function’s variables are resolved is called a closure in the computer science literature.

          看下面的例子:

          function makeCounter () {
              let counter=0;
              return function() {return counter++;};
          }
          let counter=makeCounter();
          console.log(counter());
          console.log(counter());
          console.log(counter());
          
          #+RESULTS:
          : 0
          : 1
          : 2

          對這個嵌套函數而言,最有意思的一點是:當外部函數被調用返回后(這里是makeCounter()), 再也沒有任何手段能夠觸及到 counter 這個變量。只有內嵌函數擁有專屬權限抵達該變量。


          二、Closure的標準定義

          開發者通常應該都知道“閉包”這個通用的編程術語。

          閉包 是指內部函數總是可以訪問其所在的外部函數中聲明的變量和參數,即使在其外部函數被返回(壽命終結)了之后。在某些編程語言中,這是不可能的,或者應該以特殊的方式編寫函數來實現。但是如上所述,在 JavaScript 中,所有函數都是天生閉包的(只有一個例外,將在 "new Function" 語法 中講到)。

          也就是說:JavaScript 中的函數會自動通過隱藏的 [[Environment]] 屬性記住創建它們的位置,所以它們都可以訪問外部變量。

          在面試時,前端開發者通常會被問到“什么是閉包?”,正確的回答應該是閉包的定義,并解釋清楚為什么 JavaScript 中的所有函數都是閉包的,以及可能的關于 [[Environment]] 屬性和詞法環境原理的技術細節。


          主站蜘蛛池模板: 中文字幕一区二区三区精彩视频| 国产精品女同一区二区| 无码人妻精品一区二区蜜桃AV| 激情内射日本一区二区三区| 精品一区二区三区东京热| 一区二区三区杨幂在线观看 | 色综合久久一区二区三区| 一区视频免费观看| 国产亚洲综合精品一区二区三区| 人妻无码第一区二区三区| 在线精品国产一区二区三区| 无码中文字幕乱码一区| 无码视频一区二区三区在线观看 | 在线成人一区二区| 在线精品国产一区二区| 日韩免费视频一区二区| 亚洲一区二区三区写真| 91在线精品亚洲一区二区| 久久精品国产第一区二区三区| 国产综合精品一区二区三区| 国产日韩AV免费无码一区二区| 亚洲高清偷拍一区二区三区| 亚洲免费一区二区| 精品国产不卡一区二区三区| 国产精品一区二区久久国产| 日韩av片无码一区二区三区不卡| 在线精品亚洲一区二区| 国产精品亚洲一区二区麻豆| 成人区精品一区二区不卡 | 色一情一乱一伦一区二区三欧美 | 精品国产福利一区二区| 亚洲无码一区二区三区| 精品无码中出一区二区| 一区二区在线观看视频| 国产精品一区二区无线| 精品一区二区三区在线视频观看 | 无码av中文一区二区三区桃花岛| 日韩精品一区二三区中文| 国产suv精品一区二区33| 99国产精品欧美一区二区三区| 亚洲国产精品一区二区九九|