jQuery分析(2) - $工廠函數分析
前言
從這節進入jQuery的世界,首先從jQuery的入口函數開始瞭解jQuery()或$是如何運做的,這裏我給出了一個最小的例子來分析。javascript
回憶
在進入分析代碼前咱們回想下jQuery的使用方法有哪些呢?html
- selector,[context]
- selector:用來查找的字符串
- context:做爲待查找的 DOM 元素集、文檔或 jQuery 對象。
- element
- 一個用於封裝成jQuery對象的DOM元素
- object
- 一個用於封裝成jQuery對象
- elementArray
- 一個用於封裝成jQuery對象的DOM元素數組。
- jQuery object
- 一個用於克隆的jQuery對象。
- jQuery()
- 返回一個空的jQuery對象。
你們知道$函數等於jQuery函數,在調用完$函數後它會選擇出傳遞第一個參數對應的元素或建立一個新元素,若是給定了第二個參數(context)那就是從context這個上下文向下查找。實現原理並不難,複雜的是涉及的邏輯相對多和兼容性問題。接下來是我整理的一個jQuery最小框架,咱們一塊兒來看看jQuery如何實現$('div').width()神奇的方法。java
jQuery庫本質的實現原理(對象模擬數組)源碼 官方完整源碼地址 / 簡化版源碼地址
(function() { var jQuery = function(selector) { return new jQuery.fn.init(selector); }; jQuery.fn = jQuery.prototype = { selector: "", length: 0, constructor: jQuery, find: function(selector) { var ret = []; ret.push(document.getElementById(selector)); ret = this.pushStack(ret); ret.selector = selector; return ret; }, pushStack: function(elems) { var ret = this.constructor(); for(var i = 0, il = elems.length; i < il; i++) { ret[i] = elems[i]; } ret.length = i; ret.prevObject = this; ret.context = this.context; return ret; }, msg: function() { console.log('test function'); } }; var init = jQuery.fn.init = function(selector) { if(!selector) { return this; } if(typeof selector == 'string') { return rootjQuery.find(selector); //return document.getElementById(selector); } else if(selector.nodeType) { this.context = this[0] = selector; this.length = 1; return this; } }; init.prototype = jQuery.fn; var rootjQuery = jQuery(document); window.$ = jQuery; })();
上面的代碼是我在拋開了jQuery的其餘邏輯後整理出來它的架構的基礎,接着一步一步來仔細看看他是如何運行的。node
jQuery框架核心步驟
- 對於一個jQuery對象的調用分2種:
- 新選擇一個元素或者建立一個元素(在jQuery函數中new一個jQuery.fn.init構造函數)
- 執行一個jQuery對象方法(執行jQuery在原型鏈上的方法)
- 對傳入的selector進行不一樣操做解析(不傳入任何參數將返回一個空的jQuery對象實例)
- 對於傳入一個選擇器參數進行find查找元素,對於傳入字符串參數進行DOM解析建立元素
- 建立一個新的jQuery對象,將以上選擇好的元素或者建立好的DOM節點用數組的方式合併到新的jQuery對象中並返回
$('div') 方法調用順序圖
框架代碼關鍵點
- jQuery.fn = jQuery.prototype
- jQuery.fn.init
- pushStack
jQuery.fn = jQuery.prototype 這個對象是jQuery對象的原型鏈當使用 $('div') 建立出來的jQuery對象便擁有改對象的全部方法jquery
jQuery.fn.init 函數對傳入的參數selector進行不一樣類型的解析,若是selector是一個字符串則找到他的id的元素。git
jQuery.fn.init = function(selector) { if (!selector) { return this; } if (typeof selector == 'string') { // 是字符串則找到這個字符串id的元素 return rootjQuery.find(selector); } else if (selector.nodeType) { // 若是是dom元素直接賦值和修正length,最後返回this this.context = this[0] = selector; this.length = 1; return this; } };
pushStack首先建立一個空的jQuery對象,而後把查找到的元素合併到空對象中並進行length修正,最後修改prevObject以便最上次操做的元素進行跟蹤(詳細請見end()函數)github
pushStack: function(elems) { // 建立一個空的jQuery對象 var ret = this.constructor(); // 把找到的元素放入ret空對象中 for (var i = 0, il = elems.length; i < il; i++) { ret[i] = elems[i]; } // 修正length ret.length = i; // 跟蹤上次更改元素操做 ret.prevObject = this; // 修正上下文 ret.context = this.context; // 返回裝好元素的jQuery對象 return ret; }
調用jQuery.prototype原型鏈上的msg函數示例數組
var elem = $('div'); elem.msg(); // test function
總結
分析jQuery或$函數執行流程,用簡化版例子分析jQuery從入口函數到返回jQuery對象過程當中都作了什麼事情而且對 $('div').msg() 調用方法進行了分析。經過這個簡化版例子瞭解jQuery的基礎架構。這對於之後的學習是必要。markdown