1. 程式人生 > 實用技巧 >Js中閉包的概念、原理、作用及應用

Js中閉包的概念、原理、作用及應用

一、閉包概念
閉包:有權訪問另一個函式作用域中的變數的函式,一般情況就是在一個函式中包含另一個函式。
從官方定義我們知道閉包是一個函式,只不過這個函式有[超能力],可以訪問到另一個函式的作用域。
為什麼說這個叫做[超能力]呢?
因為我們知道函式作用域是獨立的、封閉的,外部的執行環境是訪問不了的,但是閉包具有這個能力和許可權。
那閉包是怎樣的一個表現形式呢?
第一,閉包是一個函式,而且存在於另一個函式當中
第二,閉包可以訪問到父級函式的變數,且該變數不會銷燬

function person(){
  var name = '有魚';
  function cat(){
    console.log(name);
  }
  return cat;
}
var
per = person();// per的值就是return後的結果,即cat函式 per();// 有魚 per()就相當於cat() per();// 有魚 同上,而且變數name沒有銷燬,一直存在記憶體中,供函式cat呼叫 per();// 有魚

二、閉包原理
閉包的實現原理,其實是利用了作用域鏈的特性,我們都知道作用域鏈就是在當前執行環境下訪問某個變數時,如果不存在就一直向外層尋找,最終尋找到最外層也就是全域性作用域,這樣就形成了一個鏈條。
例如:

var age = 18;
function cat(){
  age++;
  console.log(age);// cat函式內輸出age,該作用域沒有,則向外層尋找,結果找到了,輸出[19];
}
cat();//19

看到這裡,大家都會說這不就是最簡單的函式和變數形式嗎?閉包在哪裡?別急,我們接著往下看:
如果我們再次呼叫時,結果會一直增加,也就變數age的值一直遞增。

cat();//20
cat();//21
cat();//22

如果程式還有其他函式,也需要用到age的值,則會受到影響,而且全域性變數還容易被人修改,比較不安全,這就是全域性變數容易汙染的原因,所以我們必須解決變數汙染問題,那就是把變數封裝到函式內,讓它成為區域性變數。

function person(){
  var age = 18;
  function cat(){
    age++;
    console.log(age);
  }
  return cat;
}
person();// 19
person();// 19

這裡又出現問題了,每次呼叫函式person,進入該作用域,變數age就會重新賦值為18,所以cat的值一直是19;所以需要做一下調整:

var per = person();//per相當於函式cat
per();// 19 即cat() 這樣每次呼叫不在經過age的初始值,這樣就可以一直增加了
per();// 20
per();// 21

而且變數age在函式內部,不易修改和外洩,相對來說比較安全。

佛山vi設計https://www.houdianzi.com/fsvi/ 豌豆資源搜尋大全https://55wd.com

三、閉包作用
作用1:隱藏變數,避免全域性汙染
作用2:可以讀取函式內部的變數
同時閉包使用不當,優點就變成了缺點:
缺點1:導致變數不會被垃圾回收機制回收,造成記憶體消耗
缺點2:不恰當的使用閉包可能會造成記憶體洩漏的問題
這裡簡單說一下,為什麼使用閉包時變數不會被垃圾回收機制收銷燬呢,這裡需要了解一下js垃圾回收機制;
js規定在一個函式作用域內,程式執行完以後變數就會被銷燬,這樣可節省記憶體;使用閉包時,按照作用域鏈的特點,閉包(函式)外面的變數不會被銷燬,因為函式會一直被呼叫,所以一直存在,如果閉包使用過多會造成記憶體銷燬。

四、閉包應用
需求:實現變數a 自增
1、通過全域性變數,可以實現,但會汙染其他程式

var a = 10;
function Add(){
  a++;
  console.log(a);
}
Add();
Add();
Add();

2、定義一個區域性變數,不汙染全域性,但是實現不了遞增

var a = 10;
function Add2(){
  var a = 10;
  a++;
  console.log(a);
}
Add2();
Add2();
Add2();
console.log(a);

3、通過閉包,可以是函式內部區域性變數遞增,不會影響全部變數,完美!!

var a = 10;
function Add3(){
  var a = 10;
  return function(){
    a++;
    return a;
  };
};
var cc = Add3();
console.log(cc());
console.log(cc());
console.log(cc());
console.log(a);