1. 程式人生 > >深入學習jquery原始碼之閉包

深入學習jquery原始碼之閉包

深入學習jquery原始碼之閉包

開發jQuery外掛的時候會用(function($){})(jQuery); 這個閉包函式來包裹

(function($) {
  // all JS code here
})(jQuery);

自執行函式寫法, 函式聲明後立即執行. 並且設定函式執行時context為this, 這裡的this因執行環境會有所不同, 瀏覽器環境應該是window.

這裡面的$只是形參,但jquery是全域性變數,所以不需要呼叫該函式就會自動執行

整個結合起來意思就是,定義了一個匿名函式,然後又呼叫該函式,該函式的實參為jQuery。

相當於:

function fun($){…};fun(jQuery);

 

因為這樣寫有以下三個好處:

  1. 避免全域性依賴
  2. 避免第三方破壞
  3. 相容jQuery操作符$和jQuery

為什麼用閉包,是因為需要區域性變數

函式外部不能訪問函式內部區域性變數(私有屬性)。因為,函式內部的變數,在函式執行完畢以後,就會被釋放掉
使用閉包,可以訪問函式的私有變數!

閉包的作用:
①可以在函式外部訪問函式的私有變數
②讓函式內部的變數可以始終存在於記憶體中,不會再函式呼叫完成後立即釋放。

閉包是一個擁有父範圍訪問許可權的函式,換名話就是,它可以使函式使用其定義外的變數。

我們不能讓別人「直接訪問」這個變數。怎麼辦呢?用區域性變數。

但是用區域性變數別人又訪問不到,怎麼辦呢?暴露一個訪問器(函式),讓別人可以「間接訪問」。

正是由於 JS 的函式內部可以使用函式外部的變數

function foo(){
  var local = 1
  function bar(){
    local++
    return local
  }
  return bar
}

var func = foo()
func()

如果不 return,你就無法使用這個閉包。把 return bar 改成 window.bar = bar 也是一樣的,只要讓外面可以訪問到這個 bar 函式就行了。所以 return bar 只是為了 bar 能被使用

 

閉包的好處:
不增加額外的全域性變數,執行過程中所有變數都是在匿名函式內部。

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();

add();
add();
add();

// the counter is now 3

 

閉包中的this和外的this都是window

<script type="text/javascript">
	var name="The Window";
	var object={
			name:"My object",
			getNameFunc:function(){
				var name="object func";
				return function(){
					return name;
				};
			}
	};
	alert(object.name);
	alert(object.getNameFunc()());
</script>

 

 

外掛不會汙染頂級物件和全域性變數,因為兩個外掛,或者多個js庫同時使用,萬一有變數函式物件是同名的就會產生衝突。

Javascript變數有本地和全域性訪問範圍,而閉包可以使全域性的變數“變成私有”。 

$(function(){

var a=1;

     })

   $(function(){

     alert(a);//undefined

   })
(function(){do someting})();
//這個你就理解為定義一個匿名函式並立即執行
帶引數的話就這樣:
(function(形參){do someting})(實參);
另外
(function(){var upc="i am upc"})();
alert(upc);
會提示undefined。
因為閉包後,裡面的變數就相當於區域性了。

定義一個公用的變數

方法一:

(1)$(function(){

            window.a=1;

     })

   $(function(){

          alert(a);

   })

方法二:

(2)

   var a=1;

   $(function(){

        alert(a);

    })

 

變數定義時不使用var關鍵字,總會被視為全域性變數,即使它在函式內定義。 

var a = 4;
function myFunction() {
    a = 10;
}
alert(a);

 

$(function(){}) 與(function($) { })(jQuery);

(function($) { })(jQuery);執行其中的程式碼時,Dom物件並不一定載入完畢。於此相反的是$(function(){}),這種方法在使用時頁面的Dom物件已經載入完畢了。事實上該方法的全寫是:$(document).ready(function(){});

頁面載入執行$(function () { });一個自呼叫函式會在定義的時候被自動呼叫,因為其後面跟著一對括號(方法執行的方式)。

當new例項化或$.fn後會執行window中原先closure中預載入和定義的方法或物件

 

物件工廠函式

var jQuery = (function(){  
  
    var jQuery = function(){  
        return new jQuery.fn.init(selector, context);  
    }  
    return (window.$ = window.jQuery = jQuery);       
});  

這裡的jQ函式返回一個新new出來的物件,相當於類的建構函式,而這個物件則是自己prototype裡的一個方法的物件,這邊的fn屬性是jQuery的原型而已,而下面的init則是其初始化函式

var jQuery = (function(){  
  
    var jQuery = function(){  
        return new jQuery.fn.init(selector, context);  
    }  
  
    jQuery.fn = jQuery.prototype = {  
        init: function(){}  
    };  
  
    return (window.$ = window.jQuery = jQuery);   
});  

我們平時用的fn的外掛實際是jQ的原型(注意:這邊返回的必須是建構函式而不是new好的物件)

上面的便是返回了一個init為建構函式的一個物件,相當於說是我們平時用的jQuery物件實質上是jQ物件自己原型中的一個建構函式物件,這個物件是init物件,並不是jQuery物件本身,所以它的原型是空的,並不包含jQ的原型,所以下面這句話異常重要。

將init的原型指向fn,而fn是指向jQ的原型,相當於init的原型是指向jQ的原型的

jQuery.fn.init.prototype = jQuery.fn;

之後程式碼變成了這樣:

var jQuery = (function(){  
  
    var jQuery = function(){  
        return new jQuery.fn.init(selector, context);  
    }  
  
    jQuery.fn = jQuery.prototype = {  
        init: function(){}  
    };  
  
    jQuery.fn.init.prototype = jQuery.fn;  
  
    return (window.$ = window.jQuery = jQuery);       
});  

再然後便可以往jQ類中加入靜態物件,放在核心內的這裡的物件或方法往往是要呼叫物件內私有屬性的,下面是對jQ名稱空間的工廠函式:

jQuery.extend = jQuery.fn.extend = function(){}

實際上也是jQ原型中定義的一個擴充套件函式

至此jQ的核心已經結束。接下來時外層閉包中的jQuery物件,這個物件首先持有了內層返回的jQ物件,而這個物件同樣其實是一個函式,或者更確切的說是一個類建構函式,然後就可以在外閉包中放一些無需依賴jQ類私有變數的庫函式。再對這個類附上庫函式。

我們平時用的$(//TODO)或是jQuery(//TODO)則是在用jQ中的init方法new 出一個jQ的物件(更確切說是init物件)

(function(window, undefined){  
<span style="white-space: pre;">    </span>//核心  
    var jQuery = (function(){})();  
  
        //非依賴私有變數的類方法  
        (function(){  
  
              //可以直接呼叫jQuery外層應用類對其進行擴充套件  
              jQuery.support = {};   
              jQuery.extend({/*TODO*/}  
  
        })();  
  
})();