《JavaScript高級程序設計》筆記:函數表達式(七)

遞歸

function factorial(num){
    if(num<=1){
        return 1;
    }else {
        return num * arguments.callee(num-1);
    }
}

console.log(factorial(4));

可是若是代碼是在嚴格模式下開發:閉包

"use strict";
function factorial(num){
    if(num<=1){
        return 1;
    }else {
        return num * arguments.callee(num-1);
    }
}

console.log(factorial(4));

結果:Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them函數

在嚴格模式下不能經過腳本訪問arguments.callee,訪問這個屬性會報錯,那麼能夠使用命名函數表達式來達到相同的結果:this

"use strict";
var factorial = (function f(num){
     if(num<=1){
        return 1;
    }else {
        return num * f(num-1);
    }
})

console.log(factorial(4)); //24

以上代碼建立了一個名爲f()的命名函數表達式,而後將它賦值給變量factorial,便是把函數賦值給另一個變量,函數的名字仍然有效。spa

閉包

閉包是指有權訪問另外一個函數做用域中的變量的函數。prototype

閉包與變量

做用域鏈的這種配置機制引出了一個值得注意的反作用,即閉包只能取得包含函數中任何變量的最後一個值。別忘了閉包所保存的是整個變量對象,而不是某個特殊的變量。code

function createFunctions(){
    var result = new Array();

    for (var i=0; i<10; i++){
        result[i] = function(){
            return i;
        }
    }

    return result;
}

咱們能夠經過建立另外一個匿名函數強制讓閉包的行爲符合預期。對象

function createFunctions(){
    var result = new Array();

    for (var i=0; i<10; i++){
        result[i] = function(num){
            return function(){
                return num;
            };
        }(i);
    }

    return result;
}

關於this對象

在全局函數中,this等於window,而當函數被做爲某個對象的方法調用時,this等於那個對象。不過,匿名函數的執行環境具備全局性,所以其this對象一般指向window。blog

var name = "The window";

var object = {
    name: "My Object",
    getNameFunc: function(){
        return function(){
            return this.name;
        };
    }
};

console.log(object.getNameFunc()()); // The window

不過,把外部做用域中的this對象保存在一個閉包可以訪問到的變量裏,就可讓閉包訪問該對象了。遞歸

var name = "The window";

var object = {
    name: "My Object",
    getNameFunc: function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};

console.log(object.getNameFunc()()); // My Object 

看下面代碼:內存

var name = "The window";
var object = {
    name: "My Object",
    getName: function(){
        console.log(this.name);
    }
}

object.getName(); // My Object 
(object.getName)(); // My Object 
(object.getName = object.getName)(); // The window

來分析下調用的結果:

第一行代碼跟日常同樣調用了object.getName()返回了My Object ,由於this.name就是object.name。

第二行代碼在調用這個方法以前給它加了一個括號。雖然加了一個括號後,就好像只是在引用一個函數,可是this的值獲得了維持,由於object.getName(object.getName)的定義是相同的。

第三行代碼先執行了一條賦值語句,而後再調用賦值後的結果。由於這個賦值表達式的值是函數自己,因此this的值不能獲得維持,結果就返回了The window

固然你不大可能像第二行和第三行代碼同樣調用這個方法。這個例子只是說明了一個細微的語法變化,都有可能意外的改變this的值。

內存泄露

function assignHandler(){
                var element=document.getElementById('someElement');
                element.onclick=function(){
                    alert(element.id);
                }
            }
            

上述代碼它所佔用的內存不會永遠消失。修改一下代碼以下解決:

function assignHandler(){
        var element = document.getElementById('someElement');
        var id = element.id;
        element.onclick = function(){
            alert(id);
        }
        element = null;
    }
    

模仿塊級做用域

用塊級做用域(一般稱爲私用做用域)的匿名函數的語法以下所示:

(function(){
})();

私有變量

function add(num1,num2){
    var sum=num1+num2;
    return sum;
}

在這個函數內部,有三個私有變量:sum,num1,num2。在函數內部能夠訪問這幾個變量。可是在函數外部則不能訪問它們。若是在這個函數內部建立一個閉包,那麼閉包能夠經過本身的做用域鏈也能夠訪問這些變量。而利用這一點,就能夠建立用於訪問私有變量的公有方法。

咱們把有權訪問私有變量和私有函數的公有方法稱爲特權方法。有兩種在對象上建立特權方法的方式。第一種是在構造函數中定義特權方法。基本模式以下:

function myObejct(){
    //私有變量和私有函數
    var privateVariable=10;

    function privateFunction(){
        return false;
    }

    //特權方法
    this.publicMethod=function(){
        privateVariable++;
        return privateFunction();
    }
}

利用私有和特權成員,能夠隱藏那些不該該被直接修改的數據,例如:

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

靜態私有變量

經過在私有做用域中定義私有變量或函數,一樣也能夠建立特權方法。其基本模式以下:

(function(){
    //私有變量和私有函數
    var privateVariable=10;

    function privateFunction(){
        return false;
    }
    //構造函數
    MyObject=function(){

    };
    //公有/特權方法
    MyObject.prototype.publicMethod=function(){
        privateVariable++;
        return privateFunction();
    }
    
})();

再看一個例子:

(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");
console.log(person1.getName()); //Nicholas
person1.setName('Grey');
console.log(person1.getName()); //Grey

var person2 = new Person("Michael");
console.log(person1.getName()); //Michael
console.log(person2.getName()); //Michael

在一個實例上調用setName()會影響全部的實例。

模塊模式

模塊模式是爲單例建立私有變量和特權方法。所謂單例,指的就是隻有一個實例的對象。按照慣例,js是以對象字面量的方式來建立單例對象的。

var singleton={
    name:value,
    method:function(){
        //這裏是方法的代碼
    }
};

模塊模式經過爲單例添加私有變量和特權方法可以使其獲得加強。其語法形式以下:

var singleton=function(){
    //私有變量和私有函數
    var privateVariable=10;

    function privateFunction(){
        return false;
    }
    //特權/公有屬性和方法
    return {
        publicProperty:true,
        publicMethod:function(){
            privateVariable++;
            return privateFunction();
        }
    }
}();

加強的模塊模式

var singleton=function(){

    //私有變量和私有函數
    var privateVariable=10;

    function privateFunction(){
        return false;
    }

    //建立對象
    var object=new CustomType();

    //添加特權/公有屬性和方法
    object.publicProperty=true;

    object.publicMethod=function(){
        privateVariable++;
        return privateFunction();
    }

    //返回這個對象
    return object;
}();
相關文章
相關標籤/搜索