1. 程式人生 > >js函式:閉包難點+this的理解

js函式:閉包難點+this的理解

1.函式的定義

1.語法

  function functionName(parameters) {
      執行的程式碼
    }

2.可以通過通過一個表示式定義,函式表示式可以儲存在變數中

var x = function (a, b) {return a * b};
var z = x(4, 3);  //這種函式實際上是一個匿名函式

3.函式的提升

myFunction(5);    //先呼叫

function myFunction(y) {      //後定義
    return y * y;
}

4.自呼叫函式:如果表示式後面緊跟 () ,則會自動呼叫。

(function () {
    var x = "Hello!!";      // 我將呼叫自己
})();

5.函式可作為一個值、一個表示式使用

function myFunction(a, b) {
    return a * b;
}

var x = myFunction(4, 3);
var x = myFunction(4, 3) * 2;

6.函式的資料型別typeof是一個物件,有屬性和方法。

2.引數

1.顯式引數
函式顯式引數在函式定義時列出,定義顯式引數時沒有指定資料型別。
2.隱式引數
函式隱式引數在函式呼叫時傳遞給函式真正的值。

我認為就是形參與實參

3.arguments 物件

  1. 在函式程式碼中,使用特殊物件 arguments[],開發者無需明確指出引數名,就能訪問它們。 舉例: 用arguments[0]可以訪問第一個引數的值,相當於陣列
  2. 引用屬性 arguments.length,可以檢測函式的引數個數
  3. 模擬函式過載
function doAdd() {
  if(arguments.length == 1) {
    alert(arguments[0] + 5);
  } else if(arguments.length == 2) {
    alert(arguments[0] + arguments[1]);
  }
}

doAdd(10);	//輸出 "15"
doAdd(40, 20);	//輸出 "60"

3.呼叫,關於this

JavaScript 函式有 4 種呼叫方式,每種方式的不同在於 this 的初始化,this指向函式執行時的當前物件。

  1. 作為函式呼叫:myFunction() 和 window.myFunction() 是一樣的,this指向全域性物件,web中的全域性物件是瀏覽器視窗windows。
  2. 作為方法呼叫:this指向的物件是myObject
var myObject = {
    firstName:"John",
    lastName: "Doe",
    fullName: function () {
        return this.firstName + " " + this.lastName;
    }
}
myObject.fullName();         // 返回 "John Doe
var myObject = {
    firstName:"John",
    lastName: "Doe",
    fullName: function () {
        return this;
    }
}
myObject.fullName();          // 返回 [object Object] (所有者物件)
//函式作為物件方法呼叫,會使得 this 的值成為物件本身

[object Object] 自定義的物件
第一個object代表使用者自定義的物件的屬性,
第二個Object代表使用者自定義的物件的方法。

3.使用建構函式呼叫函式
如果函式呼叫前使用了 new 關鍵字, 則是呼叫了建構函式。這看起來就像建立了新的函式,但實際上 JavaScript 函式是重新建立的物件

// 建構函式:
function myFunction(arg1, arg2) {
    this.firstName = arg1;
    this.lastName  = arg2;
}

   // This    creates a new object
    var x = new myFunction("John","Doe");
    x.firstName;                             // 返回 "John"
    //建構函式的呼叫會建立一個新的物件。新物件會繼承建構函式的屬性和方法。

建構函式中 this 關鍵字沒有任何的值。this 的值在函式呼叫例項化物件(new object)時建立。this指向新的物件。

4.作為函式方法呼叫函式
JavaScript 函式是物件,有方法和屬性。方法有call() 和 apply()

  • apply()方法 :接收兩個引數,第一個是函式執行的作用域(this),另一個是引數陣列。
    語法:apply([thisObj [,argArray] ]);
  • call()方法 :第一個引數和apply()方法的一樣,第一個時作用域this,但是傳遞給函式的引數必須列舉出來。
    語法:call([thisObject[,arg1 [,arg2 [,...,argn]]]]);
    舉例:
 function myFunction(a, b) {
    return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray);  // 返回 20

5. 因為巢狀的原因,巢狀函式裡的this指向windows

總結:
1、 誰呼叫了此函式, 那麼this 就指向誰
2、如果遇到函式裡面又包了一層函式,那麼 this的指向就變成全域性的了
3、 如果遇到new 那麼指向就是 新建立的例項物件
4、如果遇到call 或apply,那麼 this 指向就是 call或apply 裡面的第一個引數

4.閉包

1.作用域:函式內部可以直接讀取全域性變數,在函式外部自然無法讀取函式內的區域性變數。函式內部宣告變數的時候,一定要使用var命令。如果不用的話,實際上聲明瞭一個全域性變數!
2.如何從外部讀取區域性變數?
方法:在函式的內部,再定義一個函式。

function f1(){
    var n=999;
    function f2(){
      alert(n); // 999
    }
  }

在上面的程式碼中,函式f2就被包括在函式f1內部,這時f1內部的所有區域性變數,對f2都是可見的。但是反過來就不行,f2內部的區域性變數,對f1就是不可見的。所以,父物件的所有變數,對子物件都是可見的,反之則不成立。這就是Javascript語言特有的"鏈式作用域"結構(chain scope),子物件會一級一級地向上尋找所有父物件的變數。
3.閉包:
既然f2可以讀取f1中的區域性變數,那麼只要把f2作為返回值,我們不就可以在f1外部讀取它的內部變量了嗎!f1中一定要定義區域性變數啊!

function f1(){

    var n=999;

    function f2(){
      alert(n); 
    }

    return f2;

  }

  var result=f1();
  result(); // 999

閉包就是能夠讀取其他函式內部變數的函式。

4.閉包的作用
一個是前面提到的可以讀取函式內部的變數,另一個就是讓這些變數的值始終保持在記憶體中。所以閉包很佔記憶體!

function f1(){

    var n=999;                     //f1要用var定義,才是區域性變數!

    nAdd=function(){n+=1}       //注意:nAdd是一個全域性變數

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();                        //result實際就是閉包f2函式

  result(); // 999

  nAdd();      //nAdd的值是一個匿名函式),而這個匿名函式本身也是一個閉包

  result(); // 1000

第一次的值是999,第二次的值是1000。這證明了,函式f1中的區域性變數n一直儲存在記憶體中,並沒有在f1呼叫後被自動清除。原因就在於f1是f2的父函式,而f2被賦給了一個全域性變數,這導致f2始終在記憶體中,而f2的存在依賴於f1,因此f1也始終在記憶體中,不會在呼叫結束後,被垃圾回收機制(garbage collection)回收。

5.閉包與this
程式碼一:

var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      return function(){
        return this.name;
      };

    }

  };

  alert(object.getNameFunc()());

返回值是The Window,外面的函式指向object,當函式裡套一個函式,此時this發生了變化, 指向了全域性 window

程式碼二閉包:

var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };

    }

  };

  alert(object.getNameFunc()());

返回值是My Object。
作為方法呼叫,哪個物件呼叫this就指向哪個物件。
這是一個閉包。