ECMAScript函數表達式

——《JavaScript高級程序設計》Chapter7總結

1.匿名函數的做用

(1)動態定義函數

var sayHi;
    var a=1;
    if (a>0) {
        sayHi=function(){
            console.log("Hi");
        }
    }
    else{
        sayHi=function(){
            console.log("Yo");
        }
    }
    
    sayHi();

(2)把函數當成其餘函數的返回值來使用

function createComparisonFunction(propertyName) {
    return function(object1,object2){
        var value1=object1[propertyName];
        var value2=object2[propertyName];
        if (value1<value2) {
            return -1;
        }
        else if (value1>value2) {
            return 1;
        }
        else{
            return 0;
        }
    }
}

var data=[{name:"Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(createComparisonFunction("name"));
console.log(data[0].name);//"Nicholas"
data.sort(createComparisonFunction("age"));
console.log(data[0].name);//"Zachary"

2.遞歸

(1)經過函數聲明實現
function factorial(num) {
    if (num<=1) {
        return 1;
    }
    else{
        return num*arguments.callee(num-1);
    }
}

console.log(factorial(5));

arguments.callee是一個指向正在執行的函數的指針javascript

使用arguments.callee而非原函數名factorial能夠解決以下報錯問題:java

var anotherFactorial=factorial;
factorial=null;
anotherFactorial(5);

問題:不能在嚴格模式下訪問arguments.callee閉包

(2)經過命名函數表達式實現

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

console.log(factorial(5));

3.閉包

(1)簡介閉包

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

閉包的常見形式是在一個函數內部建立另外一個函數。this

(2)閉包的做用域鏈
通常的做用域鏈的狀況
  • 後臺每一個執行環境都有一個表示變量的對象——變量對象。全局環境的變量對象始終存在,函數這樣的局部環境的變量對象只在函數執行的過程當中存在。
  • 做用域鏈本質上是一個指向變量對象的指針列表,它只引用但不實際包含變量對象。
  • 通常來說,當某函數執行完畢後,其局部活動對象就會被銷燬,內存中僅保存全局做用域的變量對象。
閉包的狀況不同
  • 在另外一個函數內部定義的函數會將外部函數的活動對象添加到它的做用域中。
  • 在外部函數執行完畢後,其活動對象也不會被銷燬,由於其內部函數的做用域仍然在引用這個活動對象。例如上述createComparisonFunction()函數被返回後,其執行環境的做用域鏈會被銷燬,但它的活動對象仍然停留在內存中,由於其返回的匿名函數仍在引用它的活動對象;直到內部匿名函數被銷燬,createComparisonFunction()的活動對象纔會被銷燬。

閉包的內存佔用問題

由於閉包會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存。過分使用閉包可能致使內存佔用過多,故建議只在絕對須要閉包的時候再考慮用閉包。prototype

(3)閉包只能訪問包含函數中的任何變量的最後一個值

(4)做爲閉包(函數內部的函數)的匿名函數的this對象

對比如下代碼1和代碼2:設計

代碼1:指針

var name="The window";

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

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

代碼2:code

var name="The window";

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

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

緣由:對象

匿名函數的執行環境具備全局性。爲何匿名函數沒有取得其包含做用域的this對象呢?由於做用域鏈的原理,內部匿名函數在搜索this和arguments兩個特殊變量是都是從自身的活動對象開始,在搜索外部函數的活動對象,所以不可能直接訪問外不函數的這兩個變量;而其自身的this是指向全局環境的。

下面那個不是匿名函數,是函數getNameFunc()。能夠姑且這麼理解吧??

能夠把外部做用域的this用其餘變量來保存,這樣內部閉包能夠經過訪問這個變量來訪問外部做用域的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"

(5)閉包可能引發的內存泄漏

P183,待整理

4.匿名函數模仿塊級做用域

(1)JS無塊級做用域

function outputNumbers(count) {
    for (var i=0;i<count;i++) {
        console.log(i);
    }
    
    var i;
    console.log(i);
}

outputNumbers(10);//0 1 2 3 4 5 6 8 8 9 10

即便屢次聲明一個變量,JS會忽略後續的聲明(不過,它會執行後續聲明中的變量初始化)。

(2)當即執行的匿名函數

做塊級做用域(又叫私有做用域)的匿名函數語法以下:

(IIFE,(Immediately-Invoked Function Expression),當即執行的函數表達式)

function outputNumbers(count) {
    (function(){
        for (var i=0;i<count;i++) {
            console.log(i);
        }
    })();
    
    console.log(i);//(1)
}

outputNumbers(10);//0 1 2 3 4 5 6 7 8 9 報錯(語句(1)錯誤)
  • 這樣在內部匿名函數中定義的任何變量,在執行結束時都會被銷燬。故(1)不能訪問i。
  • 該匿名函數是閉包,故能夠訪問外部函數的count。
  • 這種技術繼承在全局做用域中被用在函數外部,從而限制向全局做用域添加過多的變量和函數。咱們都一個儘可能少向全局做用域添加變量和函數。由於在有多個開發人員參與的應用程序中,過多全局變量和函數容易致使命名衝突。而經過建立私有做用域,每一個開發人員可使用本身的變量,又不用擔憂搞亂全局做用域)

5.私有變量

1)私有變量概念

任何在函數中定義的變量,均可以認爲是私有變量。由於不能在函數外訪問。

私有變量包括函數的參數局部變量函數內部定義的其餘函數

2)特權方法概念

有權訪問私有變量私有函數的方法叫作特權方法

建立這樣的公有方法的思想是:在函數內部建立一個閉包,利用閉包能夠經過本身的做用域鏈訪問這些變量。

3)特權方法建立方式

(1)構造函數模式

構造函數中定義特權方法。
例一:

function MyObject() {
    var privateVariable=20;//私有變量
    function privateFunction() {//私有函數
        return false;
    }
    
    this.publicMethod=function(){//特權方法
        privateVariable++;
        return privateFunction();
    };
}

var object=new MyObject()
console.log(object.publicMethod());//false

例二:

function Person(name) {//參數爲私有變量
    this.getName=function(){//特權方法
        return name;
    }
    this.setName=function(newName){//特權方法
        name=newName;
    }
}

var aperson=new Person("Tom");

console.log(aperson.getName());//Tom
aperson.setName("Ben");
console.log(aperson.getName());//Ben
(2)原型模式

經過在私有做用域中定義私有變量和函數,也能夠建立特權方法。

(function () {
    var privateVariable=20;//私有變量
    function privateFunction() {//私有函數
        return false;
    }
    
    MyObject=function(){//構造函數,沒有var聲明就是全局變量
    };
    
    MyObject.prototype.publicMethod=function(){//公有/特權方法
         privateVariable++;
        return privateFunction();
    }
})();


var object=new MyObject();
console.log(object.publicMethod());//false
  • 該模式在定義構造函數時沒有使用函數聲明,由於函數聲明只能建立局部函數,而不帶var的函數表達式能夠建立全局函數
  • 該模式私有變量和函數是由實例共享的,因爲原型方法是在原型上定義的,故全部實例都引用同一個函數(以下例)。而特權方法做爲一個閉包保存着對包含做用域的引用。

    (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("Greg");
      console.log(person1.getName());//"Greg"
    
      var person2=new Person("Michael");
      console.log(person1.getName());//"Michael"
      console.log(person2.getName());//"Michael"
(3)模塊模式

做用:是爲單例建立私有變量和特權方法。

單例:只有一個實例的對象。

方式:在匿名函數內部,首先定義私有變量和函數,而後將一個對象字面量做爲匿名函數的返回值。返回的對象字面量裏面只包括能夠公開的屬性和方法。該對象是在匿名函數內部定義的,故它的公有方法有權訪問私有變量和函數。

var singleton=function(){
    var privateVariable=10;//私有變量
    function privateFunction() {//私有函數
        return false;
    }
    
    return {
        publicProperty:true,//特權/公有屬性
        publicMethod:function(){//特權/公有方法
            privateVariable++;
            return privateFunction();
        }
    };
}();

適用:若是必須建立一個對象並以一些數據對其進行初始化,同時還要公開一些可以訪問這些私有數據的方法,就可使用模塊模式。

(4)加強的模塊模式

對模塊模式的改進,即在返回對象以前加入對其加強的代碼。

適用於:單例必須是某種類型的實例,同時還必須添加某些屬性和方法對其加強的狀況。

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;
}();
相關文章
相關標籤/搜索