摘要:文章出處拜讀一個(gè)開源框架,最想學(xué)到的就是設(shè)計(jì)的思想和實(shí)現(xiàn)的技巧。利用下的簡單工廠模式,來將所有對(duì)于同一個(gè)對(duì)象的操作指定同一個(gè)實(shí)例。所以的中提供了以上中擴(kuò)展函數(shù)。
文章出處 http://www.cnblogs.com/aaronjs/p/3278578.html
拜讀一個(gè)開源框架,最想學(xué)到的就是設(shè)計(jì)的思想和實(shí)現(xiàn)的技巧。
廢話不多說,jquery這么多年了分析都寫爛了,老早以前就拜讀過,
不過這幾年都是做移動(dòng)端,一直御用zepto, 最近抽出點(diǎn)時(shí)間把jquery又給掃一遍
我也不會(huì)照本宣科的翻譯源碼,結(jié)合自己的實(shí)際經(jīng)驗(yàn)一起拜讀吧!
github上最新是jquery-master,加入了AMD規(guī)范了,我就以官方最新2.0.3為準(zhǔn)
整體架構(gòu)jQuery框架的核心就是從HTML文檔中匹配元素并對(duì)其執(zhí)行操作、
例如:
$().find().css() $().hide().html("....").hide().
從上面的寫法上至少可以發(fā)現(xiàn)2個(gè)問題
jQuery對(duì)象的構(gòu)建方式
2 .jQuery方法的調(diào)用方式
分析一:jQuery的無new構(gòu)建JavaScript是函數(shù)式語言,函數(shù)可以實(shí)現(xiàn)類,類就是面向?qū)ο缶幊讨凶罨镜母拍?/p>
var aQuery = function(selector, context) { //構(gòu)造函數(shù) } aQuery.prototype = { //原型 name:function(){}, age:function(){} } var a = new aQuery(); a.name();
這是常規(guī)的使用方法,顯而易見jQuery不是這樣玩的
jQuery沒有使用new運(yùn)行符將jQuery顯示的實(shí)例化,還是直接調(diào)用其函數(shù)
按照jQuery的抒寫方式
$().ready() $().noConflict()
要實(shí)現(xiàn)這樣,那么jQuery就要看成一個(gè)類,那么$()應(yīng)該是返回類的實(shí)例才對(duì)
所以把代碼改一下:
var aQuery = function(selector, context) { return new aQuery(); } aQuery.prototype = { name:function(){}, age:function(){} }
通過new aQuery(),雖然返回的是一個(gè)實(shí)例,但是也能看出很明顯的問題,死循環(huán)了!
那么如何返回一個(gè)正確的實(shí)例?
在javascript中實(shí)例this只跟原型有關(guān)系
那么可以把jQuery類當(dāng)作一個(gè)工廠方法來創(chuàng)建實(shí)例,把這個(gè)方法放到j(luò)Query.prototye原型中
var aQuery = function(selector, context) { return aQuery.prototype.init(); } aQuery.prototype = { init:function(){ return this; } name:function(){}, age:function(){} }
當(dāng)執(zhí)行aQuery() 返回的實(shí)例:
很明顯aQuery()返回的是aQuery類的實(shí)例,那么在init中的this其實(shí)也是指向的aQuery類的實(shí)例
問題來了init的this指向的是aQuery類,如果把init函數(shù)也當(dāng)作一個(gè)構(gòu)造器,那么內(nèi)部的this要如何處理?
var aQuery = function(selector, context) { return aQuery.prototype.init(); } aQuery.prototype = { init: function() { this.age = 18 return this; }, name: function() {}, age: 20 } aQuery().age //18
這樣的情況下就出錯(cuò)了,因?yàn)閠his只是指向aQuery類的,所以需要設(shè)計(jì)出獨(dú)立的作用域才行
jQuery框架分隔作用域的處理jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor "enhanced" return new jQuery.fn.init( selector, context, rootjQuery ); },
很明顯通過實(shí)例init函數(shù),每次都構(gòu)建新的init實(shí)例對(duì)象,來分隔this,避免交互混淆
那么既然都不是同一個(gè)對(duì)象那么肯定又出現(xiàn)一個(gè)新的問題
例如:
var aQuery = function(selector, context) { return new aQuery.prototype.init(); } aQuery.prototype = { init: function() { this.age = 18 return this; }, name: function() {}, age: 20 } //Uncaught TypeError: Object [object Object] has no method "name" console.log(aQuery().name())
拋出錯(cuò)誤,無法找到這個(gè)方法,所以很明顯new的init跟jquery類的this分離了
怎么訪問jQuery類原型上的屬性與方法?
做到既能隔離作用域還能使用jQuery原型對(duì)象的作用域呢,還能在返回實(shí)例中訪問jQuery的原型對(duì)象?
實(shí)現(xiàn)的關(guān)鍵點(diǎn)
// Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn;
通過原型傳遞解決問題,把jQuery的原型傳遞給jQuery.prototype.init.prototype
換句話說jQuery的原型對(duì)象覆蓋了init構(gòu)造器的原型對(duì)象
因?yàn)槭且脗鬟f所以不需要擔(dān)心這個(gè)循環(huán)引用的性能問題
var aQuery = function(selector, context) { return new aQuery.prototype.init(); } aQuery.prototype = { init: function() { return this; }, name: function() { return this.age }, age: 20 } aQuery.prototype.init.prototype = aQuery.prototype; console.log(aQuery().name()) //20
百度借網(wǎng)友的一張圖,方便直接理解:
fn解釋下,其實(shí)這個(gè)fn沒有什么特殊意思,只是jQuery.prototype的引用
分析二:鏈?zhǔn)秸{(diào)用DOM鏈?zhǔn)秸{(diào)用的處理:
1.節(jié)約JS代碼.
2.所返回的都是同一個(gè)對(duì)象,可以提高代碼的效率
通過簡單擴(kuò)展原型方法并通過return this的形式來實(shí)現(xiàn)跨瀏覽器的鏈?zhǔn)秸{(diào)用。
利用JS下的簡單工廠模式,來將所有對(duì)于同一個(gè)DOM對(duì)象的操作指定同一個(gè)實(shí)例。
這個(gè)原理就超簡單了
aQuery().init().name()
分解
a = aQuery(); a.init() a.name()
把代碼分解一下,很明顯實(shí)現(xiàn)鏈?zhǔn)降幕緱l件就是實(shí)例this的存在,并且是同一個(gè)
aQuery.prototype = { init: function() { return this; }, name: function() { return this } }
所以我們?cè)谛枰準(zhǔn)降姆椒ㄔL問this就可以了,因?yàn)榉祷禺?dāng)前實(shí)例的this,從而又可以訪問自己的原型了
aQuery.init().name()
優(yōu)點(diǎn):節(jié)省代碼量,提高代碼的效率,代碼看起來更優(yōu)雅
最糟糕的是所有對(duì)象的方法返回的都是對(duì)象本身,也就是說沒有返回值,這不一定在任何環(huán)境下都適合。
Javascript是無阻塞語言,所以他不是沒阻塞,而是不能阻塞,所以他需要通過事件來驅(qū)動(dòng),異步來完成一些本需要阻塞進(jìn)程的操作,這樣處理只是同步鏈?zhǔn)剑惒芥準(zhǔn)絡(luò)query從1.5開始就引入了Promise,jQuery.Deferred后期在討論。
分析三:插件接口jQuery的主體框架就是這樣,但是根據(jù)一般設(shè)計(jì)者的習(xí)慣,如果要為jQuery或者jQuery prototype添加屬性方法,同樣如果要提供給開發(fā)者對(duì)方法的擴(kuò)展,從封裝的角度講是不是應(yīng)該提供一個(gè)接口才對(duì),字面就能看懂是對(duì)函數(shù)擴(kuò)展,而不是看上去直接修改prototype.友好的用戶接口,
jQuery支持自己擴(kuò)展屬性,這個(gè)對(duì)外提供了一個(gè)接口,jQuery.fn.extend()來對(duì)對(duì)象增加方法
從jQuery的源碼中可以看到,jQuery.extend和jQuery.fn.extend其實(shí)是同指向同一方法的不同引用
jQuery.extend = jQuery.fn.extend = function() {}
jQuery.extend 對(duì)jQuery本身的屬性和方法進(jìn)行了擴(kuò)展
jQuery.fn.extend 對(duì)jQuery.fn的屬性和方法進(jìn)行了擴(kuò)展
通過extend()函數(shù)可以方便快速的擴(kuò)展功能,不會(huì)破壞jQuery的原型結(jié)構(gòu)
jQuery.extend = jQuery.fn.extend = function(){...};
這個(gè)是連等,也就是2個(gè)指向同一個(gè)函數(shù),怎么會(huì)實(shí)現(xiàn)不同的功能呢?這就是this 力量了!
針對(duì)fn與jQuery其實(shí)是2個(gè)不同的對(duì)象,在之前有講述:
jQuery.extend 調(diào)用的時(shí)候,this是指向jQuery對(duì)象的(jQuery是函數(shù),也是對(duì)象!),所以這里擴(kuò)展在jQuery上。
而jQuery.fn.extend 調(diào)用的時(shí)候,this指向fn對(duì)象,jQuery.fn 和jQuery.prototype指向同一對(duì)象,擴(kuò)展fn就是擴(kuò)展jQuery.prototype原型對(duì)象。
這里增加的是原型方法,也就是對(duì)象方法了。所以jQuery的api中提供了以上2中擴(kuò)展函數(shù)。
extend的實(shí)現(xiàn)
jQuery.extend = jQuery.fn.extend = function() { var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, // 常見用法 jQuery.extend( obj1, obj2 ),此時(shí),target為arguments[0] i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { // 如果第一個(gè)參數(shù)為true,即 jQuery.extend( true, obj1, obj2 ); 的情況 deep = target; // 此時(shí)target是true target = arguments[1] || {}; // target改為 obj1 // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { // 處理奇怪的情況,比如 jQuery.extend( "hello" , {nick: "casper})~~ target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { // 處理這種情況 jQuery.extend(obj),或 jQuery.fn.extend( obj ) target = this; // jQuery.extend時(shí),this指的是jQuery;jQuery.fn.extend時(shí),this指的是jQuery.fn --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // 比如 jQuery.extend( obj1, obj2, obj3, ojb4 ),options則為 obj2、obj3... // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { // 防止自引用,不贅述 continue; } // Recurse if we"re merging plain objects or arrays // 如果是深拷貝,且被拷貝的屬性值本身是個(gè)對(duì)象 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { // 被拷貝的屬性值是個(gè)數(shù)組 copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { 被拷貝的屬性值是個(gè)plainObject,比如{ nick: "casper" } clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // 遞歸~ // Don"t bring in undefined values } else if ( copy !== undefined ) { // 淺拷貝,且屬性值不為undefined target[ name ] = copy; } } } } // Return the modified object return target;
總結(jié):
通過new jQuery.fn.init() 構(gòu)建一個(gè)新的對(duì)象,擁有init構(gòu)造器的prototype原型對(duì)象的方法
通過改變prorotype指針的指向,讓這個(gè)新的對(duì)象也指向了jQuery類的原型prototype
所以這樣構(gòu)建出來的對(duì)象就繼續(xù)了jQuery.fn原型定義的所有方法了
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/85941.html
摘要:到目前為止,的貢獻(xiàn)者團(tuán)隊(duì)共名成員,多條,可想而知,是一個(gè)多么龐大的項(xiàng)目。參考源碼分析整體架構(gòu)源碼解析讀書筆記第二章構(gòu)造對(duì)象函數(shù)詳解本文在上的源碼地址,歡迎來。 歡迎來我的專欄查看系列文章。 決定你走多遠(yuǎn)的是基礎(chǔ),jQuery 源碼分析,向長者膜拜! 我雖然接觸 jQuery 很久了,但也只是局限于表面使用的層次,碰到一些問題,找到 jQuery 的解決辦法,然后使用。顯然,這種做法的...
摘要:我這里有個(gè)不夠準(zhǔn)確但容易理解的說法,就是檢查一個(gè)對(duì)象是否為另一個(gè)構(gòu)造函數(shù)的實(shí)例,為了更容易理解,下面將全部以是的實(shí)例的方式來說。 underscore源碼分析之整體架構(gòu) 最近打算好好看看underscore源碼,一個(gè)是因?yàn)樽约捍_實(shí)水平不夠,另一個(gè)是underscore源碼比較簡單,比較易讀。本系列打算對(duì)underscore1.8.3中關(guān)鍵函數(shù)源碼進(jìn)行分析,希望做到最詳細(xì)的源碼分析。今...
摘要:譯立即執(zhí)行函數(shù)表達(dá)式處理支持瀏覽器環(huán)境微信小程序。學(xué)習(xí)整體架構(gòu),利于打造屬于自己的函數(shù)式編程類庫。下一篇文章可能是學(xué)習(xí)的源碼整體架構(gòu)。也可以加微信,注明來源,拉您進(jìn)前端視野交流群。 前言 上一篇文章寫了jQuery整體架構(gòu),學(xué)習(xí) jQuery 源碼整體架構(gòu),打造屬于自己的 js 類庫 雖然看過挺多underscore.js分析類的文章,但總感覺少點(diǎn)什么。這也許就是紙上得來終覺淺,絕知此...
摘要:通常的做法是,為它們指定回調(diào)函數(shù)。請(qǐng)求返回請(qǐng)求返回請(qǐng)求返回異步隊(duì)列解耦異步任務(wù)和回調(diào)函數(shù)為模塊隊(duì)列模塊事件提供基礎(chǔ)功能。 前言 jQuery整體框架甚是復(fù)雜,也不易讀懂,這幾日一直在研究這個(gè)笨重而強(qiáng)大的框架。jQuery的總體架構(gòu)可以分為:入口模塊、底層模塊和功能模塊。這里,我們以jquery-1.7.1為例進(jìn)行分析。 jquery的總體架構(gòu) 16 (function( window,...
摘要:匿名函數(shù)將代碼包裹在里面,防止與其他代碼沖突和污染全局環(huán)境。學(xué)習(xí)整體架構(gòu),打造屬于自己的函數(shù)式編程類庫讀者發(fā)現(xiàn)有不妥或可改善之處,歡迎評(píng)論指出。 雖然現(xiàn)在基本不怎么使用jQuery了,但jQuery流行10多年的JS庫,還是有必要學(xué)習(xí)它的源碼的。也可以學(xué)著打造屬于自己的js類庫,求職面試時(shí)可以增色不少。 本文章學(xué)習(xí)的是v3.4.1 版本。unpkg.com源碼地址:https://un...
閱讀 1149·2021-09-22 15:26
閱讀 2727·2021-09-09 11:52
閱讀 2055·2021-09-02 09:52
閱讀 2315·2021-08-12 13:28
閱讀 1248·2019-08-30 15:53
閱讀 581·2019-08-29 13:47
閱讀 3469·2019-08-29 11:00
閱讀 3171·2019-08-29 10:58