jQuery
是繼prototype
以後又一個優秀的Javascript框架。它是輕量級的js庫 ,它兼容CSS3
,還兼容各類瀏覽器(IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+)
,jQuery2.0
及後續版本將再也不支持IE6/7/8
瀏覽器。jQuery使用戶能更方便地處理HTML(標準通用標記語言下的一個應用)、events、實現動畫效果,而且方便地爲網站提供AJAX交互
。jQuery還有一個比較大的優點是,它的文檔說明很全,並且各類應用也說得很詳細,同時還有許多成熟的插件可供選擇。jQuery可以使用戶的html頁面保持代碼和html內容分離,也就是說,不用再在html裏面插入一堆js來調用命令了,只需定義id便可。jquery大體能夠分爲 DOM 、 ajax 、選擇器 、 事件 、 動畫。固然jquery有13個模塊之多,這裏的5個,是從另一個角度劃分的。javascript
(令: jquery從2.0以後就不兼容IE 6/7/8 了)html
1、 整體架構
;(function(global, factory) { // 傳入window能夠將其做爲一個局部變量使用,縮短了做用域鏈,大大加快執行速度 factory(global); }(typeof window !== "undefined" ? window : this, function(window, noGlobal) { // jquery方法 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); }; // jQuery.fn 是 jquery對象的原型對象 jQuery.fn = jQuery.prototype = {}; // 核心方法 // 回調系統 // 異步隊列 // 數據緩存 // 隊列操做 // 選擇器引 // 屬性操做 // 節點遍歷 // 文檔處理 // 樣式操做 // 屬性操做 // 事件體系 // AJAX交互 // 動畫引擎 if ( typeof noGlobal === strundefined ) { window.jQuery = window.$ = jQuery; } return jQuery; }));
關於上述代碼,解釋以下:java
jQuery使用()將匿名函數括起來,而後後面再加一對小括號(包含參數列表)的目的,簡單來講就是小括號括起來後,就看成是一個表達式來處理,獲得的就是一個
function
對象了。同時小括號也取得了這個函數的引用位置,而後傳入參數就能夠直接執行了。
總結: 全局變量是魔鬼, 匿名函數能夠有效的保證在頁面上寫入JavaScript,而不會形成全局變量的污染,經過小括號,讓其加載的時候當即初始化,這樣就造成了一個單例模式的效果從而只會執行一次。
(function( global, factory ) { // 由於jquery既要再普通環境下運行,也要再例如AMD、commonJs下運行,因此咱們須要作不一樣的適應 // node CommonJs規範 if ( typeof module === "object" && typeof module.exports === "object" ) { module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { // AMD factory( global ); } // 傳入參數(window和一個執行函數) }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { // 【1】 // 建立jQuery對象, 其實是jQuery.fn.init所返回的對象 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); // 若是調用new jQuery, 生成的jQuery會被丟棄,最後返回jQuery.fn.init對象 // 所以能夠直接調用jQuery(selector, context), 不須要使用new // 若是使用new jQuery()容易出現死循環 // 咱們日常使用 $() ,就是直接調用jquery函數了 } // 【2】 // 建立jQuery對象原型,爲jQuery添加各類方法 jQuery.fn = jQuery.prototype = { ... } // 【3】 // 在調用new jQuery.fn.init後, jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype // 至關於將全部jQuery.fn的方法都掛載到一開始jQuery函數返回的對象上 // 這裏就是jquery的一個獨特之處了,很是的巧妙 jQuery.fn.init.prototype = jQuery.fn; // 【4】 // 建立jQuery.extend方法 jQuery.extend = jQuery.fn.extend = function() {、 ... } // 【5】 // 使用jQuery.extend擴展靜態方法 jQuery.extend({}); // 【6】 // 爲window全局變量添加$對象,在給window全局添加變量的時候頗有可可能會致使變量命名衝突哦,咱們以後會學習到如何處理這種命名衝突的解決方法 if ( typeof noGlobal === strundefined ) { // var strundefined = typeof undefined window.jQuery = window.$ = jQuery; // $('') // 同 jQuery('') } return jQuery; }));
2、 jQuery的類數組對象
能夠這麼理解,jquery主要的任務就是獲取DOM
。操做DOM
。node
jQuery
的入口都是經過$()
來的,經過傳入不一樣的參數,實現了9種重載方法。jquery
1. jQuery([selector,[context]]) 2. jQuery(element) 3. jQuery(elementArray) 4. jQuery(object) 5. jQuery(jQuery object) 6. jQuery(html,[ownerDocument]) 7. jQuery(html,[attributes]) 8. jQuery() 9. jQuery(callback) // 9種用法總體來講能夠分三大塊:選擇器、dom的處理、dom加載。
例子嘍ajax
經過$(".Class")構建的對象結構以下所示:\數組
<div> <span class="span">1</span> <span class="span">2</span> <span class="span">3</span> </div> console.log($('.span')); // jQuery.fn.init(3) [span.span, span.span, span.span, prevObject: jQuery.fn.init(1), context: document, selector: ".span"] // 0:span.span // 1:span.span // 2:span.span // context: document // length: 3 // prevObject: jQuery.fn.init [document, context: document] // selector:".span" // __proto__:Object(0)
// 模擬一下 function Ajquery(selecter) { // 若是傳入的不是一個對象,則將其轉換爲對象 if(!(selecter instanceof Ajquery)) { return new Ajquery(selecter); } var elem = document.getElementById(/[^#].*/.exec(selector)[0]); // 獲取id this[0] = elem; this.length = 1; this.context = document; this.selector = selector; this.get = function(num) { return this[num]; } return this; } // 使用 $('#show2').append(Ajquery('#book').get(0)); // 所以 $('')獲取到的就是一個類數組對象
jQuery的無new構造原理promise
咱們在構造jQuery對象的時候,並無使用new來建立,但實際上是在jQuery
方法的內部,咱們使用了new
,這樣就保證了當前對象內部就又了一個this對象,而且吧全部的屬性和方法的鍵值對都映射到this上了,因此既能夠經過鏈式取值,也能夠經過索引取值。jquery除了實現了類數組結構
, 方法的原型共享
,還實現了靜態和實例的共享
.瀏覽器
javascript就是函數式語言
,函數能夠實現類,因此javascript不是一個嚴格的面向對象的語言。緩存
function ajquery(name){ this.name = name; } ajquery.prototype = function(){ say: function(){ return this.name; } } var a = new ajquery(); a.say();
$().ready() $().noConflict()
var aQuery = function(selector, context) { return new aQuery(); // 直接new一下 } aQuery.prototype = { name:function(){}, age:function(){} } // 若是是上訴的樣子,直接new aQuery()則會致使死循環。
// 下面就是jquery的寫法了 jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }, // 可是問題又來了,init中的this指向的是實例init的原型,就導師了jquery類的this分離了, // 解決這個問題的方法是: jQuery.fn.init.prototype = jQuery.fn;
以上就是jQuery無new構造的原理了
// 精簡分析 var ajquery = function(name) { return new ajquery.prototype.init(name); } ajquery.prototype = { init: function(name) { this.name = name; return this; }, get: function() { return this.name; }, name: 'zjj' } ajquery.prototype.init.prototype = ajquery.prototype;//這裏使得init內部的this跟ajquery類的this保持了一致。 console.log(ajquery('zmf').get()); // zmf
3、 ready和load事件
針對於文檔的加載
// 一 $(function() { }) // 二 $(document).ready(function() { }) // 三 $(document).load(function() { })
在上面咱們看到了一個是ready
一個是load
,那麼這兩個有什麼區別呢?
// 咱們先來看一個寫DOM文檔的加載過程吧 1. html 解析 2. 加載外部引用腳本和外部樣式 3. 解析執行腳本 4. 構造DOM原型 // ready 5. 加載圖片等文件 6. 頁面加載完畢 // load
document.addEventListener("DOMContentLoaded", function () { console.log('DOMContentLoaded回調') }, false); // 當初始的 HTML 文檔被徹底加載和解析完成以後,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。 window.addEventListener("load", function () { console.log('load事件回調') }, false); console.log('腳本解析一') //測試加載 $(function () { console.log('腳本解析二') }) console.log('腳本解析三') // 觀察腳本加載的順序 // test.html:34 腳本解析一 // test.html:41 腳本解析三 // test.html:38 腳本解析二 // test.html:26 DOMContentLoaded回調 // test.html:30 load事件回調
看完上面的過程咱們不難看出ready是在文檔加載完畢也就是DOM建立完畢後執行的,而load則是在頁面加載完畢以後才執行的。
兩者惟一的區別就是中間加了一個圖片的加載,,可是圖片等外部文件的加載確實很慢的呢。
在平時種咱們爲了增長用戶的體驗效果,首先應該執行的是咱們的處理框架的加載。而不是圖片等外部文件的加載。咱們應該越早處理DOM越好,咱們不須要等到圖片資源都加載後纔去處理框架的加載,這樣就能增長用戶的體驗了。
// 源碼分析 jQuery.ready.promise = function( obj ) { if ( !readyList ) { readyList = jQuery.Deferred(); if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready ); } else { document.addEventListener( "DOMContentLoaded", completed, false ); window.addEventListener( "load", completed, false ); } } return readyList.promise( obj ); };
DOM文檔是否加載完畢處理方法
當HTML文檔內容加載完畢後觸發,並不會等待圖像、外部引用文件、樣式表等的徹底加載。
<script> document.addEventListener("DOMContentLoaded", function(event) { console.log("DOM fully loaded and parsed"); }); for(var i=0; i<1000000000; i++){ // 這個同步腳本將延遲DOM的解析。 // 因此DOMContentLoaded事件稍後將啓動。 } </script>
該事件的瀏覽器支持狀況是在IE9及以上支持。
兼容不支持該事件的瀏覽器
在IE8中可以使用readystatechange來檢測DOM文檔是否加載完畢。
對於跟早的IE,能夠經過每隔一段時間執行一次document.documentElement.doScroll("left")
來檢測這一狀態,由於這條代碼在DOM加載完畢以前執行時會拋出錯誤(throw an error)。
document.onreadystatechange = subSomething;//當頁面加載狀態改變的時候執行這個方法. function subSomething() { if(document.readyState == "complete"){ //當頁面加載狀態爲徹底結束時進入 //你要作的操做。 } } // 用這個能夠作一下等待網站圖片或者其餘東西加載完之後的操做,好比加載時咱們能夠調用加載動畫,當complete也就是加載完成時咱們讓加載動畫隱藏,這樣只是一個小例子。仍是很完美的。
針對IE的加載檢測
Diego Perini 在 2007 年的時候,報告了一種檢測 IE 是否加載完成的方式,使用 doScroll 方法調用,詳情可見http://javascript.nwbox.com/I...。
原理就是對於 IE 在非 iframe 內時,只有不斷地經過可否執行 doScroll 判斷 DOM 是否加載完畢。在上述中間隔 50 毫秒嘗試去執行 doScroll,注意,因爲頁面沒有加載完成的時候,調用 doScroll 會致使異常,因此使用了 try -catch 來捕獲異常。
結論:因此總的來講當頁面 DOM 未加載完成時,調用 doScroll 方法時,會產生異常。那麼咱們反過來用,若是不異常,那麼就是頁面DOM加載完畢了。
// Ensure firing before onload, maybe late but safe also for iframes document.attachEvent( "onreadystatechange", completed ); // A fallback to window.onload, that will always work window.attachEvent( "onload", completed ); // If IE and not a frame // continually check to see if the document is ready var top = false; try { // 非iframe中 top = window.frameElement == null && document.documentElement; } catch(e) {} if ( top && top.doScroll ) { (function doScrollCheck() { if ( !jQuery.isReady ) { try { // Use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ top.doScroll("left"); } catch(e) { // 每一個50ms執行一次 return setTimeout( doScrollCheck, 50 ); } // 分離全部dom就緒事件 detach(); // and execute any waiting functions jQuery.ready(); } })(); }
3、 解決$的衝突
解決方案–– noConflict函數
。
引入jQuery運行這個noConflict
函數將變量$的控制權讓給第一個實現它的那個庫,確保jQuery不會與其餘庫的$對象發生衝突。
在運行這個函數後,就只能使用jQuery
變量訪問jQuery對象
。例如,在要用到$("aaron")
的地方,就必須換成jQuery("aaron")
,由於$的控制權已經讓出去了。
// jquery導入 jQuery.noConflict(); // 使用 jQuery jQuery("aaron").show(); // 使用其餘庫的 $() // 別的庫導入 $("aaron").style.display = ‘block’;
這個函數必須在你導入jQuery文件以後,而且在導入另外一個致使衝突的庫以前使用。固然也應當在其餘衝突的庫被使用以前,除非jQuery是最後一個導入的。
(function(window, undefined) { var // Map over jQuery in case of overwrite // 設置別名,經過兩個私有變量映射了 window 環境下的 jQuery 和 $ 兩個對象,以防止變量被強行覆蓋 _jQuery = window.jQuery, _$ = window.$; jQuery.extend({ // noConflict() 方法讓出變量 $ 的 jQuery 控制權,這樣其餘腳本就可使用它了 // 經過全名替代簡寫的方式來使用 jQuery // deep -- 布爾值,指示是否容許完全將 jQuery 變量還原(移交 $ 引用的同時是否移交 jQuery 對象自己) noConflict: function(deep) { // 判斷全局 $ 變量是否等於 jQuery 變量 // 若是等於,則從新還原全局變量 $ 爲 jQuery 運行以前的變量(存儲在內部變量 _$ 中) if (window.$ === jQuery) { // 此時 jQuery 別名 $ 失效 window.$ = _$; } // 當開啓深度衝突處理而且全局變量 jQuery 等於內部 jQuery,則把全局 jQuery 還原成以前的情況 if (deep && window.jQuery === jQuery) { // 若是 deep 爲 true,此時 jQuery 失效 window.jQuery = _jQuery; } // 這裏返回的是 jQuery 庫內部的 jQuery 構造函數(new jQuery.fn.init()) // 像使用 $ 同樣盡情使用它吧 return jQuery; } }) }(window)
使用實例:
<script src="prototype.js"></script>//1.包含jQuery以外的庫(好比Prototype) <script src="jquery.js"></script>//2.包含jQuery庫取得對$的使用權 <script> jQuery.noConflict();//3.調用noConflict()方法,讓出$,把控制權讓給最早包含的庫 </script> <script src="myscript.js"></script>
讓出$控制權後,須要使用jQuery方法時,則不能用$來調用了,要用jQuery。或者經過定義新的名稱來代替$符號。
var jq=jQuery.noConflict();
另外還有一個技巧,能夠再.ready()方法中使用$。它的回調函數能夠接收一個參數,這個參數爲jQuery對象自己,能夠從新命名jQuery爲$,這樣也是不會形成衝突的。
jQuery.(document).ready(function($){ //這裏能夠正常使用$ })
jQuery源碼學習系列