這篇文章主要介紹了關於jquery原始碼學習之jQuery的核心內容,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
一、物件的建構
// 方法一 function ajquery (name) { this.name = name; this.sayName = function() { return this.name; } return this; } // 方法二 function ajquery(name) { this.name = name; } ajquery.prototype = { sayName : function () { return this.name; } }
上面是兩種創建類別的方式,雖然最後實現的效果是一致的但是在效能上確實不一致的,當我們實例化三個ajquery物件的時候,對於方法一,每個實例都有一個sayName方法,這樣就浪費了內存,增加了開銷,方法二則是吧sayName放到了原型鏈上了,這樣每一個實例對像都能共享這個方法了(是一個不錯的選擇呢),只是透過scope連接到原型鏈上查找,這樣就無形之中也就是多了一層作用域鏈的查找;
jquery的物件在性能上考慮,所以就必須採用原型鏈了呢
jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); } jQuery.fn = jQuery.prototype = { init:function(){ return this }, jquery: version, constructor: jQuery, // ……………… } var a = $() ;
二、 jquery中的物件的建構器的分離
new一個實例的過程
1. 创建一个空对象 2. 将构造函数的作用域赋给这个对象,这个this就指向了这个对象了 3. 执行构造函数中的代码 4. 返回这个对象
new主要是吧原型鍊跟實例的this連接起來
我們常用的類別式寫法如下:
var $$ = ajquery = function (selector) { this.selector = selector; return this; } ajquery.fn = ajquery.prototype = { getSelector: function () { return this.selector; }, constructor: ajquery } var a = new $$('aaa'); a.getSelector();
如果不適用new同樣也可以實作
var $$ = ajquery = function (selector) { if(!(this instanceof ajquery)) { return new ajquery(selector); } this.selector = selector; return this; }
jquery中的寫法
var $$ = ajquery = function(selector) { return new ajquery.fn.init(selector); } ajquery.fn = ajquery.prototype = { name: 'meils', init: function (selector){ console.log(this); }, constructor: ajquery }
init是ajQuery原型上作為建構器的一個方法,那麼其this就不是ajQuery了,所以this就完全引用不到ajQuery的原型了,所以這裡透過new把init方法與ajQuery給分離成2個獨立的構造器。
三、 方法的鍊式呼叫
$('input[type="button"]') .eq(0).click(function() { alert('点击我!'); }).end().eq(1) .click(function() { $('input[type="button"]:eq(0)').trigger('click'); }).end().eq(2) .toggle(function() { $('.aa').hide('slow'); }, function() { $('.aa').show('slow'); }); // end()方法是将最近的筛选操作还原为前一步操作的状态
看這個程式碼的結構,我們或多或少都能猜到其意義:
☑ 找出type類型為button的input元素
☑ 找到第一個按鈕,並綁定click事件處理函數
☑ 回傳所有按鈕,再找到第二個
☑ 為第二個按鈕綁定click事件處理函數
☑ 為第三個按鈕綁定toggle事件處理函數
jquery的核心概念是寫的少,辦的多。
jquery.fn.init = function() { return this; } jquery.fn.get = function() { return this; }
// 透過返回this來實現鍊式操作,因為傳回目前實例的this,從而又可以存取自己的原型了,這樣的就節省程式碼量,提高程式碼的效率,程式碼看起來更優雅。但是這種方法有一個問題是:所有物件的方法回傳的都是物件本身,也就是說沒有回傳值,所以這種方法不一定在任何環境下都適合。
雖然Javascript是無阻塞語言,但是他並不是沒阻塞,而是不能阻塞,所以他需要透過事件來驅動,異步來完成一些本需要阻塞進程的操作,這樣處理只是同步鍊式,除了同步鍊式還有非同步鍊式,非同步鍊式jQuery從1.5開始就引進了Promise,jQuery.Deferred後期再討論。
四、插件介面的設計
jQUery為插件的編寫提供了兩個介面,一種時$.extend()
將其作為靜態方法處理,另一種時將方法掛在到 $.fn
上,作為jquery的原型物件的方法。
fn與jQuery其實是2個不同的對象,在之前有講解:jQuery.extend 呼叫的時候,this是指向jQuery對象的(jQuery是函數,也是對象!),所以這裡擴展在jQuery上。而jQuery.fn.extend 呼叫的時候,this指向fn對象,jQuery.fn 和jQuery.prototype指向同一對象,擴展fn就是擴展jQuery.prototype原型對象。這裡增加的是原型方法,也就是物件方法了。所以jQuery的API中提供了以上2個擴充函數。
五、外掛程式開發
jquery外掛的開發模式一共有三種。
$.extend() 來擴充jquery靜態方法
#$.fn 新增新的實例方法
$.widget()應用jquery ui的部件工廠方式
第一種方式只是在$的命名空間下創建了一個靜態方法,可以直接使用$來呼叫執行即可。而不需要使用$('selector')來選取DOM物件再來呼叫方法。第三種方法是用來寫更進階的外掛程式使用的,一般不使用。
第二種方法是我們平時開發所,最常用的一種形式,稍後我們將仔細學習該方法
$.extend()
$.extend({ sayName: function(name) { console.log(name); } }) $.sayName('meils'); // 直接使用 $.sayName 来调用 // 通过$.extend()向jQuery添加了一个sayHello函数,然后通过$直接调用。到此你可以认为我们已经完成了一个简单的jQuery插件了。
這種方法用來定義一些輔助方法,還是有用的。
$.extend({ log: function(message) { var now = new Date(), y = now.getFullYear(), m = now.getMonth() + 1, //!JavaScript中月分是从0开始的 d = now.getDate(), h = now.getHours(), min = now.getMinutes(), s = now.getSeconds(), time = y + '/' + m + '/' + d + ' ' + h + ':' + min + ':' + s; console.log(time + ' My App: ' + message); } }) $.log('initializing...'); //调用
$.fn 開發外掛
一、基本使用
$.fn.setColor = function() { this.css('color', 'red'); } //这里的this指向调用我们这个方法的那个对象
二、each()遍歷
#我們也可以對我們所獲得的元素的集合的每一個元素使用我們的方法。 ###$.fn.addUrl = function() { this.css('css', 'red'); this.each(function() { $(this).append('i'); }) }######jquery選擇器###選取的是一個集合,因此可以###$.each()###來遍歷每一個元素,在###each###內部,###this###`指的就是每一個DOM元素了,如果需要呼叫jquery的方法,那麼就需要在用###$####來包裝一下了;####### #三、鍊式呼叫######
$.fn.setSelector = function() { this.css('color', 'red'); return this.each(function() { }) }######四、參數的接收#######
我们会使用$.extend()来处理我们参数,因为这个方法如果传入多个参数后,他会把这些合并到第一个上。如果存在有同名参数,那么遵循后面的覆盖前面的。
// 利用这一点,我们可以在插件里定义一个保存插件参数默认值的对象,同时将接收来的参数对象合并到默认对象上,最后就实现了用户指定了值的参数使用指定的值,未指定的参数使用插件默认值。 $.fn.setStyle = function(options) { var default = { color: 'red', fontSize: '15px' } var setting = $.extend(default, optioins); return this.css({ 'color': setting.color, 'fontSize': setting.fontSize }) }
上面的参数处理有一点不足,我们把default也改变了,万一我们接下来还要使用该怎么办呢,所以我们还需要在做修改
$.fn.setStyle = function(options) { var default = { color: 'red', fontSize: '15px' } var setting = $.extend({}, default, optioins); return this.css({ 'color': setting.color, 'fontSize': setting.fontSize }) }
五、面向对象开发插件
你可能需要一个方法的时候就去定义一个function,当需要另外一个方法的时候,再去随便定义一个function,同样,需要一个变量的时候,毫无规则地定义一些散落在代码各处的变量。
还是老问题,不方便维护,也不够清晰。当然,这些问题在代码规模较小时是体现不出来的。
// 如果将需要的重要变量定义到对象的属性上,函数变成对象的方法,当我们需要的时候通过对象来获取,一来方便管理,二来不会影响外部命名空间,因为所有这些变量名还有方法名都是在对象内部。 var beautify = function(ele, option) { this.$element = this.ele; this.default = { 'color': 'red', 'fontSize': '12px', 'textDecoration':'none' } this.ops = $.extend({}, default, option); } beautify.prototype = { setStyle : function() { return this.$element.css({ 'color': this.options.color, 'fontSize': this.options.fontSize, 'textDecoration': this.options.textDecoration }) // return this对象,实现链式调用 } } $.fn.myPlugin = function(option) { var beautify = new beautify(this, option); beautify.setStyle(); } /// 使用 $('a').myPlugin({ 'color': '#2C9929', 'fontSize': '20px' });
六、解决命名冲突
因为随着你代码的增多,如果有意无意在全局范围内定义一些变量的话,最后很难维护,也容易跟别人写的代码有冲突。
比如你在代码中向全局window对象添加了一个变量status用于存放状态,同时页面中引用了另一个别人写的库,也向全局添加了这样一个同名变量,最后的结果肯定不是你想要的。所以不到万不得已,一般我们不会将变量定义成全局的。
一个最好的方法就是始终使用自调用的匿名函数来包裹你的代码,这样,就可以完全放心的使用自己的变量了。
绝对不会有命名冲突。
;(function() { var beautify = function(ele, option) { this.$element = this.ele; this.default = { 'color': 'red', 'fontSize': '12px', 'textDecoration':'none' } this.ops = $.extend({}, default, option); } beautify.prototype = { setStyle : function() { return this.$element.css({ 'color': this.options.color, 'fontSize': this.options.fontSize, 'textDecoration': this.options.textDecoration }) // return this对象,实现链式调用 } } $.fn.myPlugin = function(option) { var beautify = new beautify(this, option); beautify.setStyle(); } })();
优化一
var foo=function(){ //别人的代码 }//注意这里没有用分号结尾 //开始我们的代码。。。 (function(){ //我们的代码。。 alert('Hello!'); })();
由于前面的代码没有加;
号 , 然后我们的插件加载出错了,所以我们在我们插件的最开始加一个;
号来强制前面的结束。
优化二
我们可以将一些系统变量传入我们的插件,这样可以缩短变量的作用域链,将其作为我们插件内部的局部变量来使用,这样可以大大提高我们的速度和性能。
;(function($, window, document, undefined) { var beautify = function(ele, option) { this.$element = this.ele; this.default = { 'color': 'red', 'fontSize': '12px', 'textDecoration':'none' } this.ops = $.extend({}, default, option); } beautify.prototype = { setStyle : function() { return this.$element.css({ 'color': this.options.color, 'fontSize': this.options.fontSize, 'textDecoration': this.options.textDecoration }) // return this对象,实现链式调用 } } $.fn.myPlugin = function(option) { var beautify = new beautify(this, option); beautify.setStyle(); } })(jQuery, window,document,);
一个安全,结构良好,组织有序的插件编写完成。
六、版本回溯
jquery能过够方便的获取到DOM元素,并且能够遍历它们。你知道这些背后的原理吗?
通过对sizzle的分析,我们知道jquery获取到的是一个jquery对象,是一个包装容器。
你会看到在上面有一个prevObject
对象。
在jquery内部维护着一个jquery对象栈,遍历方法每一次遍历都会找到一个新的元素,每一个元素都是一个jquery对象,然后jquery会把这些元素都放到这个栈中。(入栈)
$('ul').find('li'); // 这句话可以拆分,第一个jquery对象是$('ul'); 第二个jquery对象是$('ul').find('li'); // 首先将$('ul')入栈 // 然后将li集合(类数组对象)入栈
因为栈中的每一个元素都是一个jquery对象,每一个jquery对象都有着三个属性,分别时context
、selector
、prevObject
, prevObject
属性就是指向对象栈中的前一个元素的。
这个prevObject
就比较有意思了。它指向前一个元素。
举例子了:
jquery为我们操纵对象栈提供了两个有用的方法
end()
addSelf()
这两个方法都是进行版本的回退。
end()
返回最近的一次筛选的上一个状态。回到最近的一个"破坏性"操作之前。即,将匹配的元素列表变为前一次的状态。
<p><span>Hello</span>,how are you?</p> $('p').find('span').end(); // 选取所有的p元素,查找并选取span子元素,然后再回过来选取p元素
end()
方法返回的就是一个prevObject
;
addBank
.addBack()方法导致前一组遍历堆栈中的DOM元素被添加到当前组。
来举例子喽
// First Example $("p.before-addback").find("p").addClass("background"); // p元素添加背景 // Second Example $("p.after-addback").find("p").addBack().addClass("background"); // 选中的p元素以及前一个元素合并到栈中,全部添加背景
利用这个DOM元素栈可以减少重复的查询和遍历的操作,而减少重复操作也正是优化jQuery代码性能的关键所在。
在对对象栈的操作中,用到了一个pushStack()
pushStack 生成了一个新 jQuery 对象 ret,ret 的 prevObject 属性是指向调用 pushStack 函数的那个 jQuery 对象的,这样就形成了一个栈链,其它原型方法如 find、nextAll、filter 等都可以调用 pushStack 函数,返回一个新的 jQuery 对象并维持对象栈。
jQuery.fn.pushStack = function (elems) { // 将 elems 合并到 jQuery 对象中 var ret = jQuery.merge(this.constructor(), elems); // 实现对象栈 ret.prevObject = this; // 返回 return ret; }
end()源码解析
jQuery.fn.end = function() { return this.prevObject || this.constructor; // 如果存在之前的jquery对象就返回它,如果不存在上一个,就返回当前的jQuery对象。 }
addBank() 源码解析
jQuery.fn.addBank = function(selector) { return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector)); // 如果参数为空,就把当前的jquery对象的上一个jQuery对象一起合并为一个新的对象。如果指定了参数,那么就在上一个对象中查找这个对象。 }
以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP中文網!
相關推薦:
################################################################################################################################# ####以上是jquery源碼的核心內容的詳細內容。更多資訊請關注PHP中文網其他相關文章!