JS閉包的理解及常見應用場景


JS閉包的理解及常見應用場景

一、總結

一句話總結:

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

 

1、如何從外部讀取函數內部的變量,為什么?

閉包:f2可以讀取f1中的變量,只要把f2作為返回值,就可以在f1外讀取f1內部變
原因:f1是f2的父函數,f2被賦給了一個全局變量,f2始終存在內存中,f2的存在依賴f1,因此f1也始終存在內存中,不會在調用結束后,被垃圾回收機制回收。
function f1(){
        var n = 123;
        function f2(){    //f2是一個閉包
            alert(n)
        }    
        return f2;
    }

js鏈式作用域:子對象會一級一級向上尋找所有父對象的變量,反之不行。

 

 

2、js鏈式作用域?

子對象會一級一級向上尋找所有父對象的變量,反之不行。
js中函數內部可以讀取全局變量,函數外部不能讀取函數內部的局部變量。

 

 

3、js變量兩種作用域?

全局變量、局部變量(函數內):js中函數內部可以讀取全局變量,函數外部不能讀取函數內部的局部變量。

 

 

4、閉包為什么可以實現在函數外讀取到函數內的變量?

|||-begin

function f1(){
        var n = 123;
        function f2(){    //f2是一個閉包
            alert(n)
        }    
        return f2;
    }

|||-end

原因:f1是f2的父函數,f2被賦給了一個全局變量,f2始終存在內存中,f2的存在依賴f1,因此f1也始終存在內存中,不會在調用結束后,被垃圾回收機制回收。

 

 

 

二、對JS閉包的理解及常見應用場景

轉自或參考:對JS閉包的理解及常見應用場景
https://blog.csdn.net/qq_21132509/article/details/80694517

 

1、變量作用域

變量作用域兩種:全局變量、局部變量。js中函數內部可以讀取全局變量,函數外部不能讀取函數內部的局部變量。

2、如何從外部讀取函數內部的變量?

function f1(){
        var n = 123;
        function f2(){    //f2是一個閉包
            alert(n)
        }    
        return f2;
    }
js鏈式作用域:子對象會一級一級向上尋找所有父對象的變量,反之不行。
f2可以讀取f1中的變量,只要把f2作為返回值,就可以在f1外讀取f1內部變量

3、閉包概念

能夠讀取其他函數內部變量的函數。
或簡單理解為定義在一個函數內部的函數,內部函數持有外部函數內變量的引用。

4、閉包用途

1、讀取函數內部的變量
2、讓這些變量的值始終保持在內存中。不會再f1調用后被自動清除。
3、方便調用上下文的局部變量。利於代碼封裝。
原因:f1是f2的父函數,f2被賦給了一個全局變量,f2始終存在內存中,f2的存在依賴f1,因此f1也始終存在內存中,不會在調用結束后,被垃圾回收機制回收。

5、閉包理解

/** * [init description] * @return {[type]} [description] */
function init() {
    var name = "Chrome";    //創建局部變量name和局部函數alertName

    function alertName() { //alertName()是函數內部方法,是一個閉包
        alert(name); //使用了外部函數聲明的變量,內部函數可以訪問外部函數的變量
    }
    alertName();
}
init();
//一個變量在源碼中聲明的位置作為它的作用域,同時嵌套的函數可以訪問到其外層作用域中聲明的變量

/** * [outFun description] * @return {[type]} [description] */
function outFun(){
    var name = "Chrome";
    function alertName(){
        alert(name);
    }
    return alertName;   //alertName被外部函數作為返回值返回了,返回的是一個閉包
}

var myFun = outFun();
myFun();
/* 閉包有函數+它的詞法環境;詞法環境指函數創建時可訪問的所有變量。 myFun引用了一個閉包,閉包由alertName()和閉包創建時存在的“Chrome”字符串組成。 alertName()持有了name的引用, myFunc持有了alertName()的的訪問, 因此myFunc調用時,name還是處於可以訪問的狀態。 */


/** * [add description] * @param {[type]} x [description] */
function add(x){
    return function(y){
        return x + y;
    };
}

var addFun1 = add(4);
var addFun2 = add(9);

console.log(addFun1(2)); //6
console.log(addFun2(2));  //11
//add接受一個參數x,返回一個函數,它的參數是y,返回x+y
//add是一個函數工廠,傳入一個參數,就可以創建一個參數和其他參數求值的函數。
//addFun1和addFun2都是閉包。他們使用相同的函數定義,但詞法環境不同,addFun1中x是4,后者是5

6、閉包應用場景之setTimeout


    //原生的setTimeout傳遞的第一個函數不能帶參數
    setTimeout(function(param){
        alert(param)
    },1000)


    //通過閉包可以實現傳參效果
    function func(param){
        return function(){
            alert(param)
        }
    }
    var f1 = func(1);
    setTimeout(f1,1000);

7、閉包應用場景之回調

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <link rel="stylesheet" href="">
</head>
<style> body{ font-size: 12px; } h1{ font-size: 1.5rem; } h2{ font-size: 1.2rem; } </style>
<body>

    <p>哈哈哈哈哈哈</p>
    <h1>hhhhhhhhh</h1>
    <h2>qqqqqqqqq</h2>

    <a href="#" id="size-12">12</a>
    <a href="#" id="size-14">14</a>
    <a href="#" id="size-16">16</a>

<script> function changeSize(size){ return function(){ document.body.style.fontSize = size + 'px'; }; } var size12 = changeSize(12); var size14 = changeSize(14); var size16 = changeSize(16); document.getElementById('size-12').onclick = size12; document.getElementById('size-14').onclick = size14; document.getElementById('size-16').onclick = size16; //我們定義行為,然后把它關聯到某個用戶事件上(點擊或者按鍵)。我們的代碼通常會作為一個回調(事件觸發時調用的函數)綁定到事件上 </script>
</body>
</html>

8、閉包應用場景之封裝變量

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>閉包模擬私有方法</title>
    <link rel="stylesheet" href="">
</head>
<body>
<script> //用閉包定義能訪問私有函數和私有變量的公有函數。 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(); counter.increment();//2 //共享的環境創建在一個匿名函數體內,立即執行。 //環境中有一個局部變量一個局部函數,通過匿名函數返回的對象的三個公共函數訪問。 </script>
</body>
</html>

9、閉包應用場景之為節點循環綁定click事件

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <link rel="stylesheet" href="">
</head>
<body>

    <p id="info">123</p>
    <p>E-mail: <input type="text" id="email" name="email"></p>
    <p>Name: <input type="text" id="name" name="name"></p>
    <p>Age: <input type="text" id="age" name="age"></p>

<script> function showContent(content){ document.getElementById('info').innerHTML = content; }; function setContent(){ var infoArr = [ {'id':'email','content':'your email address'}, {'id':'name','content':'your name'}, {'id':'age','content':'your age'} ]; for (var i = 0; i < infoArr.length; i++) { var item = infoArr[i]; document.getElementById(item.id).onfocus = function(){ showContent(item.content) } } } setContent() //循環中創建了三個閉包,他們使用了相同的詞法環境item,item.content是變化的變量 //當onfocus執行時,item.content才確定,此時循環已經結束,三個閉包共享的item已經指向數組最后一項。 /** * 解決方法1 通過函數工廠,則函數為每一個回調都創建一個新的詞法環境 */ function showContent(content){ document.getElementById('info').innerHTML = content; }; function callBack(content){ return function(){ showContent(content) } }; function setContent(){ var infoArr = [ {'id':'email','content':'your email address'}, {'id':'name','content':'your name'}, {'id':'age','content':'your age'} ]; for (var i = 0; i < infoArr.length; i++) { var item = infoArr[i]; document.getElementById(item.id).onfocus = callBack(item.content) } } setContent() /** * 解決方法2 綁定事件放在立即執行函數中 */ function showContent(content){ document.getElementById('info').innerHTML = content; }; function setContent(){ var infoArr = [ {'id':'email','content':'your email address'}, {'id':'name','content':'your name'}, {'id':'age','content':'your age'} ]; for (var i = 0; i < infoArr.length; i++) { (function(){ var item = infoArr[i]; document.getElementById(item.id).onfocus = function(){ showContent(item.content) } })()//放立即執行函數,立即綁定,用每次的值綁定到事件上,而不是循環結束的值 } } setContent() /** * 解決方案3 用ES6聲明,避免聲明提前,作用域只在當前塊內 */ function showContent(content){ document.getElementById('info').innerHTML = content; }; function setContent(){ var infoArr = [ {'id':'email','content':'your email address'}, {'id':'name','content':'your name'}, {'id':'age','content':'your age'} ]; for (var i = 0; i < infoArr.length; i++) { let item = infoArr[i]; //限制作用域只在當前塊內 document.getElementById(item.id).onfocus = function(){ showContent(item.content) } } } setContent() </script>
</body>
</html>

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM