6-作用域鏈中變量的使用原則 閉包 回調(diào)/惰性/即時(shí)函數(shù)

作用域鏈中變量的使用原則

  • 在作用域鏈中使用(讀取/設(shè)置)變量的時(shí)候, 首先在當(dāng)前作用域鏈中查找, 找到就直接使用
  • 如果沒(méi)有找到, 就去上一級(jí)作用域鏈中查找, 直到0級(jí)作用域

閉包

  • 一般情況下作用域: 內(nèi)層作用域可以訪(fǎng)問(wèn)外層作用域, 反之不行

      function f1(){
         var n=999;
         function f2(){  
            alert(n); // 999
          }
        }
    
  • 有時(shí)候需要從外層作用域訪(fǎng)問(wèn)內(nèi)層作用域

    1. 直接return, 一次性獲取數(shù)據(jù), 每次獲取的不是同一份數(shù)據(jù)(變量創(chuàng)建后使用后就銷(xiāo)毀了)

          function f1(){
             var n=999;
             function f2(){
               alert(n);
             }
             return f2;
          }
         var result=f1();
         result(); // 999
      
    2. 閉包技術(shù): 可以間接訪(fǎng)問(wèn)封閉空間私有數(shù)據(jù)的方法

      • 閉包就是對(duì)直接return的數(shù)據(jù)進(jìn)行包裝(函數(shù))
  • 閉包獲取數(shù)據(jù)和設(shè)置數(shù)據(jù)

    • 判斷不傳形參的情況, 形參和變量不能同名 (同名的話(huà), 形參在調(diào)用賦值時(shí), 會(huì)先從當(dāng)前作用域找同名變量)

閉包的好處

  • 獲取數(shù)據(jù)只能通過(guò)指定的方法(接口)
  • 在設(shè)置數(shù)據(jù)的時(shí)候更加安全, 可以做一些校驗(yàn)工作
  • 延長(zhǎng)變量的生命周期

setTimeout和閉包的執(zhí)行

//第一種寫(xiě)法
for(var i=0; i<3; i++){
    (function(index){
        setTimeout(function(){
            console.log(index+'+++');
        },0);
    })(i);
}
//第二種寫(xiě)法
for(var i=0; i<3; i++){
    setTimeout((function(index){
        return function(){
            console.log(index+'----');
        }
    })(i),0);
}
//第三種方法
for(let i=0; i<3; i++){
    setTimeout(function(){            
        console.log(i+'----');            
    },1000);
}

div事件和閉包

  • JS的任務(wù)

    1. 渲染任務(wù)
    2. 代碼的主要任務(wù)
    3. 事件性的任務(wù)(點(diǎn)擊,定時(shí)器..)
  • JS是單線(xiàn)程

    1. 進(jìn)程: 正在運(yùn)行的應(yīng)用程序 (工廠)
    2. 線(xiàn)程: 進(jìn)程中用來(lái)執(zhí)行任務(wù)的(工人),一個(gè)線(xiàn)程同一時(shí)間只能執(zhí)行一個(gè)任務(wù)
    3. 串行執(zhí)行: 多個(gè)任務(wù)一個(gè)一個(gè)的按順序執(zhí)行
    4. 并發(fā)執(zhí)行: 多個(gè)任務(wù)同時(shí)執(zhí)行
    5. 多程線(xiàn):多條線(xiàn)程
    6. 多線(xiàn)程的原理: 1s = 1萬(wàn)個(gè)0.0001s cpu在多個(gè)任務(wù)之間來(lái)回的快速切換,造成多個(gè)任務(wù)同時(shí)執(zhí)行的假象
    //JS單線(xiàn)程 先循環(huán)完 再觸發(fā)點(diǎn)擊事件時(shí) i遍歷完自加1變成5了--所以當(dāng)點(diǎn)擊div時(shí)變成5了
    for (var i = 0; i < divs.length; i++) {
        divs[i].onclick = function () {
            console.log('點(diǎn)擊了第' + i + '個(gè)div');
        }
    }
    //方法一:閉包解決拿到i的值
    for (var i = 0; i < divs.length; i++) {    
        (function (j) {
            divs[j].onclick = function () {
                console.log('點(diǎn)擊了第' + j+ '個(gè)div');
            }
        })(i);    
    }
    //方法二
    for (var i = 0; i < divs.length; i++) {
        divs[i].onclick = (function (j) {
            return function () {
                console.log('點(diǎn)擊了第' + j + '個(gè)div');
            }
        })(i);
    }

函數(shù)的特殊性

  • 特殊性(特點(diǎn)): 函數(shù)本身是對(duì)象, 且對(duì)象可以提供作用域
    • 函數(shù)可以在運(yùn)行時(shí)動(dòng)態(tài)的創(chuàng)建, 還可以在程序執(zhí)行過(guò)程中創(chuàng)建
    • 函數(shù)可以賦值給變量, 可以被擴(kuò)展, 甚至刪除
    • 函數(shù)可以作為其他函數(shù)的參數(shù)/返回值
    • 函數(shù)可以擁有自己的屬性和方法
  • 注意
    • {}塊在JS中不會(huì)創(chuàng)建作用域, 哪怕是if或者是while語(yǔ)句中使用var關(guān)鍵字申明的變量也并非局部變量, 函數(shù)是可以通過(guò)()調(diào)用并執(zhí)行對(duì)象
  • 函數(shù)是第一類(lèi)型對(duì)象
    • 函數(shù)可以向普通對(duì)象一樣, 作為函數(shù)的參數(shù) | 賦值給變量(函數(shù)表達(dá)式) | 作為函數(shù)的返回值返回

    • fun.name函數(shù)名可以獲取, 但不能修改

        //函數(shù)作為參數(shù)傳遞
        setTimeout(function () {
            console.log(1);
        },100);
      
        //函數(shù)作為返回值
        function func() {
            return function () {
                console.log("demo");
            }
        }       
        var f = func();
        f();                //demo
      
        //函數(shù)賦值給變量
        var a = function(){}
        a();    //直接通過(guò)變量的名稱(chēng)調(diào)用函數(shù)
      

回調(diào)函數(shù)

  • 定義: 把一個(gè)函數(shù)作為其他函數(shù)的參數(shù)
    • 把函數(shù)作為另一個(gè)函數(shù)的參數(shù)
    //01 提供一個(gè)對(duì)象,該對(duì)象中永遠(yuǎn)showName方法
    var obj = {
        name:"默認(rèn)的名字",
        age:30,
        showName:function () {
            console.log(this.name);
        },
        showAge:function () {
            console.log(this.age);
        }
    };                          

    //02 提供一個(gè)函數(shù),該函數(shù)接受一個(gè)參數(shù)(函數(shù)引用)
    function demo(callBack,callBack_obj) {
        //處理第一個(gè)參數(shù)傳遞對(duì)象方法字符串的形式
        if(typeof callBack == 'string') {
            callBack = callBack_obj[callBack];
        }

        if (typeof callBack == 'function') {
            callBack.call(callBack_obj);
        }
    }

    //demo(obj.showName,obj);
    //demo(obj.showAge,obj);

    //傳遞字符串和對(duì)象來(lái)進(jìn)行調(diào)用
    demo("showName",obj);

    //(01)以上代碼傳入兩個(gè)參數(shù),分別為具體的回調(diào)函數(shù),和該回調(diào)函數(shù)所屬的對(duì)象
    //(02)該函數(shù)的參數(shù)接受兩種方式的回調(diào)傳遞(一種是直接傳遞函數(shù)引用,一種是直接以字符串的方式傳遞對(duì)象方法的字符串)
    //(03)在函數(shù)內(nèi)部對(duì)傳入的回調(diào)參數(shù)做處理,修正this的問(wèn)題
  • 函數(shù)作為返回值
    • 使用閉包實(shí)現(xiàn)一個(gè)計(jì)數(shù)器(在該示例中setup函數(shù)的返回值為一個(gè)函數(shù))
    • 通過(guò)調(diào)用返回值(一個(gè)函數(shù)),可以操作setup函數(shù)中的變量
    <script>
        var setup = function () {
            var count = 0;
            return function () {
                return count ++;
            }
        }
    
        var next = setup();
        console.log(next());    //0
        console.log(next());    //1
        console.log(next());    //2
    </script>

惰性函數(shù)

  • 定義: 函數(shù)真正的內(nèi)容需要執(zhí)行一次函數(shù)才能確定, 實(shí)現(xiàn)自我更新

          function foo() {
              console.log("foo!");
              //函數(shù)是引用型數(shù)據(jù), foo的存儲(chǔ)地址被重新定義了
              foo = function () { 
                  console.log("new foo!");
              }
          }
      
          //函數(shù)的調(diào)用
          //foo();  //foo!
          //foo();  //new foo!
    
  • 應(yīng)用場(chǎng): 函數(shù)有一些初始化的準(zhǔn)備工作要做,且只需要執(zhí)行一次的情況。

  • 注意點(diǎn)

    • 在函數(shù)上添加的屬性或方法, 自我更新后無(wú)法訪(fǎng)問(wèn)
    • 把惰性函數(shù)賦值給變量, 以變量的(對(duì)象的方法)方式來(lái)調(diào)用, 不會(huì)更新(調(diào)用仍然是同一份數(shù)據(jù))


      惰性函數(shù)以對(duì)象的形式訪(fǎng)問(wèn).png

      惰性函數(shù)賦值給變量.png

即時(shí)函數(shù)

  • 定義: 在函數(shù)定義之后立即執(zhí)行該函數(shù)。

           //第一種寫(xiě)法
           (function () {
               console.log("即時(shí)函數(shù)的第一種寫(xiě)法");
           })();
    
           //第二種寫(xiě)法
           ;(function () {
                console.log("即時(shí)函數(shù)的第二種寫(xiě)法");
           })();
          
           //補(bǔ)充寫(xiě)法
           (function (a) {
                console.log(a);
           }(20));
    
           +function (b) {
                 console.log(b);
           }(30);
    
           -function (b) {
                console.log(b);
           }(40);
    
  • 模式組成

    • 使用函數(shù)表達(dá)式來(lái)定義函數(shù)(匿名函數(shù), 不能使用函數(shù)聲明方式)
    • 在函數(shù)表達(dá)式末尾添加一組(), 表示立即執(zhí)行當(dāng)前函數(shù)
    • 將整個(gè)函數(shù)包裝在()中, 有兩種方式
  • 作用

    • 用來(lái)將所有的代碼包裝到當(dāng)前的作用域中,并且不會(huì)將任何的變量泄露到全局作用域中。
    • js中沒(méi)有代碼塊作用域,而函數(shù)是js中唯一可以創(chuàng)建作用域的。
    • 即時(shí)函數(shù)就是利用了函數(shù)創(chuàng)建作用域這一點(diǎn),來(lái)實(shí)現(xiàn)對(duì)一些需要封裝且不允許外部訪(fǎng)問(wèn)的操作。
  • 優(yōu)點(diǎn)

    • 不會(huì)產(chǎn)生全局變量,在即時(shí)函數(shù)內(nèi)部定義的所有變量都僅僅只是該函數(shù)的局部變量,不會(huì)造成全局變量污染問(wèn)題。
    • 具有更好的封裝性,外部無(wú)法訪(fǎng)問(wèn)到該函數(shù)內(nèi)部的數(shù)據(jù)。
  • 即時(shí)函數(shù)的傳參和返回值

       //01 接受參數(shù)
       (function (str) {
           console.log(str);           //hello
       })("hello");
    
       //02 提供返回值并賦值給新的變量
       var foo = (function () {
          return 2 + 1;
        })();
    
       console.log(foo);           //3
    

即時(shí)對(duì)象初始化

  • 結(jié)構(gòu)特征
    • 提供一個(gè)對(duì)象, 在該對(duì)象內(nèi)部提供一個(gè)init初始化方法
    • 使用()把對(duì)象包裝起來(lái)(讓字面量變成表達(dá)式)
    • 然后隨即調(diào)用init方法, 完成初始化操作
  • 基本結(jié)構(gòu)
    • 即時(shí)對(duì)象初始化: ({init: function(){}}).init();
  • 模式優(yōu)點(diǎn)
    • 在執(zhí)行一次性的初始化任務(wù)時(shí)保護(hù)全局的命名空間
({
    name:"張三",
    age:23,
    getDescript:function () {
    console.log("名字:" + this.name + "年齡:" + this.age);
},

//注意:在對(duì)象中訪(fǎng)問(wèn)對(duì)象的屬性和方法都需要使用this.前綴
init:function () {
    this.getDescript();
    //其他的初始化處理
}
}).init();

設(shè)計(jì)模式: 為了解決開(kāi)發(fā)中遇到的一類(lèi)問(wèn)題而提出的一套方法

  • 要求: 一般一套系統(tǒng)需要設(shè)計(jì)模式
  • 來(lái)源: 建筑行業(yè)
  • 工廠模式核心
    • 提供一個(gè)父構(gòu)造函數(shù)-->開(kāi)了一家工廠
    • 設(shè)置父構(gòu)造函數(shù)的原型對(duì)象-->產(chǎn)品公共東西
    • 在父構(gòu)造函數(shù)上提供一個(gè)靜態(tài)的工廠方法-->生產(chǎn)產(chǎn)品
      1. 接受傳入的參數(shù)
      2. 判斷是否支持生產(chǎn)
      3. 設(shè)置子構(gòu)造函數(shù)的原型對(duì)象是父構(gòu)造函數(shù)的一個(gè)實(shí)例(原型鏈繼承)
      4. 利用子構(gòu)造函數(shù)創(chuàng)建對(duì)象并且返回
    • 定制合作伙伴
    • 利用父構(gòu)造函數(shù)的靜態(tài)工廠方法創(chuàng)建對(duì)象
  • 作用
    • 通過(guò)統(tǒng)一的方法/接口創(chuàng)建對(duì)象, 并且根據(jù)不同的類(lèi)型創(chuàng)建不同的對(duì)象, 便于代碼的維護(hù)和擴(kuò)展
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容