1. 程式人生 > >[Javascript 高階程式設計]學習心得記錄10 js函式表示式

[Javascript 高階程式設計]學習心得記錄10 js函式表示式

    在前面說物件的時候已經提到了函式物件,對函式的定義引數的傳遞包括通過argumentd.callee實現遞迴。這篇部落格我會繼續深入講解js中的函式表示式。

一,閉包

    關於閉包的概念,可以先看看http://www.jb51.net/article/24101.htm,閉包是指有權訪問另一個函式作用域中的變數的函式。在剛剛這個連結中對閉包的用法說的很清楚了,我感覺我沒法說得比這個文章更簡單更清楚了。但是,還是有幾點很重要的東西這個文章沒講的,我補充一下。

1,變數

閉包只能取得包含函式中任何變數的最後一個值,舉例就是

            function createFunctions(){
                var result = new Array();
                
                for (var i=0; i < 10; i++){
                    result[i] = function(){
                        return i;
                    };
                }
                
                return result;
            }
            
            var funcs = createFunctions();
            
            //every function outputs 10
            for (var i=0; i < funcs.length; i++){
                document.write(funcs[i]() + "<br />");  //輸出10個10
            }
但是,我們可以通過建立另一個匿名函式強制讓閉包的行為符合預期
            function createFunctions(){
                var result = new Array();
                
                for (var i=0; i < 10; i++){
                    result[i] = function(num){
                        return function(){
                            return num;
                        };
                    }(i);
                }
                
                return result;
            }
            
            var funcs = createFunctions();
            
            //every function outputs 10
            for (var i=0; i < funcs.length; i++){
                document.write(funcs[i]() + "<br />");
            }
原理就是在for迴圈裡面增加一個匿名函式,然後把這個匿名函式的值返回給陣列。在迴圈內部,在呼叫每個匿名函式時,就形成了閉包,i的值得到了儲存並且存到了數組裡面。

2,this物件

在閉包使用this物件可能導致一些問題:匿名函式的執行環境具有全域性性,那麼看例子

        var name = "The Window";
        
        var object = {
            name : "My Object",
        
            getNameFunc : function(){
                return function(){
                    return this.name;
                };
            }
        };
        
        alert(object.getNameFunc()());  //"The Window"
有一種比較蠢的辦法解決,就是先把包含作用域儲存到另一個變數:
            var name = "The Window";
            
            var object = {
                name : "My Object",
            
                getNameFunc : function(){
                    var that = this;
                    return function(){
                        return that.name;
                    };
                }
            };
            
            alert(object.getNameFunc()());  //"MyObject"
3,記憶體洩露

實際上,閉包儲存包含函式作用域的變數利用的就是前面說過的垃圾回收機制,所有被引用的變數都不會被回收,但是總是不回收勢必導致一個問題,記憶體洩漏。解決辦法也很簡單,很普通的方法,釋放該變數就行了。用完之後賦值null。

二,模仿塊級作用域
前面說過了,js是沒有塊級作用域的。但是很多情況下我們需要一個私有作用域,這種情況下可以利用函式作用域來實現。先定義一個匿名函式,然後馬上執行這個函式。

            function outputNumbers(count){
            
                (function () {
                    for (var i=0; i < count; i++){
                        alert(i);
                    }
                })();
                
                alert(i);   //causes an error
            }
值得注意的是,函式外部的小括號非常重要,加上這個小括號才能把函式宣告轉化成函式表示式,只有函式表示式才能在後面接小括號立即執行。

三,私有變數
函式內部的變數可以通過閉包的方式儲存在記憶體中,而外部作用域是無法直接修改的,除非在函式內部增加修改這些變數的函式介面,這些介面就是特權方法。只有通過特權方法才能修改這些私有變數。

            function Person(name){
            
                this.getName = function(){
                    return name;
                };
            
                this.setName = function (value) {
                    name = value;
                };
            }
            
            var person = new Person("Nicholas");
            alert(person.getName());   //"Nicholas"
            person.setName("Greg");
            alert(person.getName());   //"Greg"
不過,在建構函式中定義特權方法讓程式碼複用性很低,原因和前面說過的建立物件一樣,建構函式每次例項時都會建立同樣的一組新方法。下面介紹新的方法。

1,靜態私有變數

之前是通過原型物件解決的,這次也可以一樣。

            (function(){
            
                var name = "";
                
                Person = function(value){                
                    name = value;                
                };
                
                Person.prototype.getName = function(){
                    return name;
                };
                
                Person.prototype.setName = function (value){
                    name = value;
                };
            })();
            
            var person1 = new Person("Nicholas");
            alert(person1.getName());   //"Nicholas"
            alert(person1.name);
            alert(person1.getName());   //"Greg"
                               
            var person2 = new Person("Michael");
            alert(person1.getName());   //"Michael"
            alert(person2.getName());   //"Michael"
2.模組模式

沒搞明白,以後再說。