丸子學JS(學習1小時 - 閉包的應用)

何為閉包

如果一個函數訪問了此函數的父級及父級以上的作用域變量,那這個函數就是一個閉包

本質上, JS中的每個函數都是一個閉包,因為每個函數都可以訪問全局變量

閉包的執(zhí)行過程

function a() {
    var i = '初始值';
    i = i + "—_執(zhí)行a"
    // 此處的函數b訪問了父級函數a中的局部變量i,成為了一個閉包
    function b() {
        i = i + "_執(zhí)行b"
        console.log(i)
    }
    return b;
}
var c = a(); // 此時 i 的值為 :初始值—_執(zhí)行a
c()          // 此時 i 的值為 :初始值—_執(zhí)行a_執(zhí)行b
c()          // 此時 i 的值為 :初始值—_執(zhí)行a_執(zhí)行b_執(zhí)行b
  1. 將函數a賦值給全局變量c時,a會執(zhí)行一次,局部變量 i 的值變?yōu)槌跏贾怠?em>執(zhí)行a,最終返回函數b,此時全局變量c的值為閉包函數b的引用。
    此時函數a雖然已執(zhí)行完,但因為內部包含閉包函數b,所以函數 a 的執(zhí)行期上下文會繼續(xù)保留在內存中,不會被銷毀,所以局部變量 i 仍是初始值—執(zhí)行a

執(zhí)行期上下文:當函數執(zhí)行時,會創(chuàng)建一個執(zhí)行期上下文的內部對象。每調用一次函數,就會創(chuàng)建一個新的上下文對象,他們之間是相互獨立的。當函數執(zhí)行完畢,它所產生的執(zhí)行期上下文會被銷毀

  1. 第一次執(zhí)行 c() 時,閉包函數b第一次執(zhí)行,局部變量 i 的值變?yōu)槌跏贾怠?em>執(zhí)行a執(zhí)行b
  2. 第二次執(zhí)行 c() 時,閉包函數b第二次執(zhí)行,局部變量 i 的值變?yōu)槌跏贾怠?em>執(zhí)行a執(zhí)行b_執(zhí)行b

圖解閉包

var a = "global variable";
var F = function () {
  var b = "local variable";
  var N = function () {
    var c = "inner local";
    return b;
  };
 return N;
};
var d = F()
d()

● 全局作用域 G 中有:
○ —— 函數 F
○ —— 全局變量 a
○ —— 全局變量 d (存有對閉包函數 N 的引用)
● 函數 F 中有:
○ —— 返回閉包函數N
○ —— 函數 F 作用域中的局部變量 b
○ —— 閉包函數 N
● 閉包函數 N 中有:
○ —— 返回局部變量b
○ —— 函數 N 作用域中的局部變量 c

閉包的特點

  1. 被閉包函數訪問的父級及以上的函數的局部變量會一直存在于內存中, 不會被JS的垃圾回收機制回收

2.閉包函數實現(xiàn)了對其他函數內部變量的訪問。(函數內部的變量對外是無法訪問的,閉包通過這種變通的方法,實現(xiàn)了訪問。)

閉包的用途

1.訪問函數內部的變量
2.讓變量始終保持在內存中

閉包的應用場景

模擬面向對象的代碼風格

模擬兩人對話

function person(name) {
    function say(content) {
        console.log(name + ':' + content)
    }
    return say
}

a = person("張三")
b = person("李四")
a("在干啥?")
b("沒干啥。")
a("出去玩嗎?")
b("去哪?。?)

================打印start================
張三:在干啥?
李四:沒干啥。
張三:出去玩嗎?
李四:去哪???    
================打印end================
使setTimeout支持傳參

通過閉包實現(xiàn)setTimeout第一個函數傳參(默認不支持傳參)

function func(param){
    return function(){
        alert(param)
    }
}
var f1 = func(1);
setTimeout(f1,1000);
封裝私有變量
//用閉包定義能訪問私有函數和私有變量的公有函數。
var counter = (function () {
    var privateCounter = 0; //私有變量
    function change(val) {
        privateCounter += val;
    }
    return {
        increment: function () {
            change(1);
        },
        decrement: function () {
            change(-1);
        },
        value: function () {
            return privateCounter;
        }
    };
})();

console.log(counter.value());//0
counter.increment();
console.log(counter.value());//1
counter.increment();
console.log(counter.value());//2
模擬塊作用域
var elements = document.getElementsByTagName('li');
var length = elements.length;
for (var i = 0; i < length; i++) {
    elements[i].onclick = function (num) {
        return function () {
            alert(num);
        };
    }(i);
}
實現(xiàn)迭代器
function setup(x) {
  var i = 0;
  return function(){
    return x[i++];
  };
}
var next = setup(['a', 'b', 'c']);

================打印start===============
> next();
"a"
> next();
"b"
> next();
"c"
================打印end===============

閉包的優(yōu)點

  1. 可以減少全局變量的定義,避免全局變量的污染
  2. 能夠讀取函數內部的變量
  3. 在內存中維護一個變量,可以用做緩存

閉包的缺點

  1. 造成內存泄露
  2. 閉包可能在父函數外部,改變父函數內部變量的值
  3. 造成性能損失

閉包范例

返回匿名閉包
function funA(){
  var a = 10;  // funA的活動對象之中;
  return function(){   //匿名函數的活動對象;
        alert(a);
  }
}
var b = funA();
b();  //10
各自獨立的閉包
function outerFn(){
  var i = 0; 
  function innerFn(){
      i++;
      console.log(i);
  }
  return innerFn;
}
var inner = outerFn();  //每次外部函數執(zhí)行的時候,都會開辟一塊內存空間,外部函數的地址不同,都會重新創(chuàng)建一個新的地址
inner();
inner();
inner();
var inner2 = outerFn();
inner2();
inner2();
inner2();   //1 2 3 1 2 3
訪問全局變量的閉包
var i = 0;
function outerFn(){
  function innnerFn(){
       i++;
       console.log(i);
  }
  return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2();     //1 2 3 4
閉包的鏈式調用
var add = function (x) {
    var sum = 1;
    var tmp = function (x) {
        console.log('執(zhí)行tmp')
        sum = sum + x;
        return tmp;
    }
    tmp.toString = function () {
        return sum;
    }
    return tmp;
}
console.log(add(1)(2)(3).toString())

===============打印start================
執(zhí)行tmp
執(zhí)行tmp
6
===============打印end================
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容