天天看點

Javascript 中 作用域、閉包與 this 指針

内容預覽

             1. js 中的作用域

             2.閉包的了解和運用

             3.this 的了解和運用

【一】 js中的作用域 

           一段簡單的代碼來說明

var outFunc = function() {
            var out_name = "outFunc";
            var out_num = 130;

            var inFunc = function() {
                var in_name = "inFunc";
                var in_num = 3;

                alert(out_name);
                alert(out_num);
                alert(in_name);
                alert(in_num);
            };

            inFunc();
            // 調用 inFunc 函數執行的結果是,彈出:outFunc、130、inFunc、3
            
            alert(out_name);
            alert(out_num);
            alert(in_name);
            alert(in_num);
        };
        outFunc();
        // 調用 inFunc 函數執行的結果是,彈出:outFunc、130、undefined、undefined
           

            内部函數可以通路外部函數的變量,外部不能通路内部函數的變量。上面的例子中内部函數inFunc可以通路變量 out_name和out_num,而外部函數 outFunc 不可以通路 inFunc 中的變量 in_name和in_num,是以會抛出沒有定義變量的異常。

             很重要的一點,如果忘記 var,那麼變量或者函數就會被聲明為全局變量或者全局函數了。下面例子的寫法,in_name 變量和 test 函數 是全局的變量和全局函數,不屬内部作用域了。

var outFunc = function() {
            var out_name = "outFunc";
            var out_num = 130;
            var inFunc = function() {
                 in_name = "inFunc";
                 var in_num = 3;
            };
            test function(){
                 alert('Test');
            }
        };
           

【二】 閉包的了解和運用 

            閉包這個概念,在函數式程式設計裡很常見,簡單的說常見的兩大用處,一是使外部函數可以突破作用域進而通路定義在内部函數中的變量,二是讓變量的值始終儲存在記憶體中。當在一個函數中定義另個函數就會産生閉包。如果要深入了解閉包,可以檢視這篇部落格http://coolshell.cn/articles/6731.html

//javascript 中特殊形式的函數,自調用函數
        var a = function() {
            function setUp() {
                //定義方法時可以做首次的初始化,這個隻會調用一次
                alert('初始化');
            }
            setUp();
            function doSomething() {
                alert('要執行的操作')
            }
            return doSomething; //這裡傳回的不是一個函數,而是一個引用
        }(); //加()進行首次調用初始化,既首次執行setUp函數(自調用函數)
        // a(); //彈出 初始化
        // a(); //彈出  要執行的操作
           
//閉包的應用一: 通過閉包突破全局作用域鍊
        var n;
        function f() {
            var a = 'king'; //這是局部變量
            n = function() { //外部可通過 n 函數通路局部變量a   
                return a;
            };
            //這個test函數 不加var 預設為全局的函數,加var 則為局部的
            test = function() {
                alert('預設全局函數');
            }
        }
           
//閉包的應用二: 定義私有變量的取值和指派
        var setValue, getValue;
        (function() {
            var n = 0; //私有變量
            getValue = function() {
                return n;
            }
            setValue = function(x) {
                n = x;
            }
        })(); //這樣寫為自調用函數
           
//閉包的應用三:疊代器        
        function test(x) {
            var i = 0;
            return function() {
                return x[i++];
            }
        }
        var next = test(['a', 'b', 'c', 'd']);
        // alert(next());//彈出a
        // alert(next());//彈出b
        // alert(next());//彈出c
        // alert(next());//彈出d
           
//了解不到位常犯的錯誤,這種錯誤通常不容易被發現
        function f() {
            var a = [];
            var i;
            for (var i = 0; i < 3; i++) {
                a[i] = function() {
                    return i; //這裡隻是對 i 這個指針進行了引用
                };
            }
            return a;
        }
        var test = f();
        // alert(test[0]());//彈出 3
        // alert(test[1]());//彈出 3
        // alert(test[2]());//彈出 3
        //出現這個問題原因在于 i 是一個指針,也就是一個位址,閉包隻是引用了這個指針。當for 循環周遊結束 指針 i 指向的值為 3
        // 可采用自調用函數的方式來避免上述的問題,如下所示
        function f() {
            var a = [];
            var i;
            for (var i = 0; i < 3; i++) {
                a[i] = (function(x) {
                    return function() {
                        return x;
                    }
                })(i);
            }
            return a;
        }
        var test = f();
        alert(test[0]()); //彈出 0
        alert(test[1]()); //彈出 1
        alert(test[2]()); //彈出 2 
           

【三】this 的了解和運用

          在函數執行時,this 總是指向調用該函數的對象。要判斷 this 的指向,其實就是判斷 this 所在的函數屬于誰。

          在《javaScript語言精粹》這本書中,把 this 出現的場景分為四類,簡單的說就是:

        1) 有對象就指向調用對象            

var myObject = {value:111};
        myObject.getValue = function(){
            console.log(this.value);//輸出100
            console.log(this);//輸出 { value: 100, getValue: [Function] }
        }
        myObject.getValue();
       // getValue() 屬于對象 myObject,并由 myOjbect 進行 . 調用,是以 this 指向對象 myObject。
           

          2) 沒調用對象就指向全局對象           

var myObject = { value: 100 };
        myObject.getValue = function() {
            var foo = function() {
                console.log(this.value) // 輸出 undefined
                console.log(this); // 輸出全局對象 global  
                 //foo 函數雖然定義在 getValue 的函數體内,但實際上它既不屬于 getValue 也不屬于 myObject。
                // foo 并沒有被綁定在任何對象上,是以當調用時,它的 this 指針指向了全局對象 global。
            };
            foo();
            return this.value; //這個this 在 getValue中,進而指向 myObject。
        };
        console.log(myObject.getValue()); // 輸出 100 
           

          3) 用new構造就指向新對象             

//js 中,我們通過 new 關鍵詞來調用構造函數,此時 this 會綁定在該新對象上。
        var SomeClass = function() {
            this.value = 100;
        }
        var myCreate = new SomeClass();
        console.log(myCreate.value); // 輸出100
           

          4) 通過 apply 或 call 或 bind 來改變 this 的所指

// apply 和 call 調用以及 bind 綁定: 指向綁定的對象
        // apply() 方法接受兩個參數第一個是函數運作的作用域, 另外一個是一個參數數組(arguments)。
        // call() 方法第一個參數的意義與 apply() 方法相同, 隻是其他的參數需要一個個列舉出來。
        // 簡單來說, call 的方式更接近我們平時調用函數, 而 apply 需要我們傳遞 Array 形式的數組給它。 它們是可以互相轉換的。
        var myObject = { value: 100 };
        var foo = function() {
            console.log(this);
        };
        foo(); // 全局變量 global
        foo.apply(myObject); // { value: 100 }
        foo.call(myObject); // { value: 100 }
        var newFoo = foo.bind(myObject);
        newFoo(); // { value: 100 }</span>
           

繼續閱讀