jquery原理解析,简述jquery的实现原理

请教一个jquery load函数的原理

1:表现形式:

创新互联-专业网站定制、快速模板网站建设、高性价比三亚网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式三亚网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖三亚地区。费用合理售后完善,十多年实体公司更值得信赖。

传统的:

javascript的加载事件:

body onload = “函数()”

window.onload= function(){}

jquery :

① $(document).ready(function处理);

② $().ready(function处理);

③ $(function处理); 对第一种加载的封装而已

2:设置个数:

在同一个请求里边,jquery的可以设置多个,而传统方式只能设置一个

传统方式加载事件是给onload事件属性赋值,多次赋值,后者会覆盖前者。

jquery方式加载事件是把每个加载事件都存入一个数组里边,成为数组的元素,执行的时候就遍历该数组执行每个元素即可,因此其可以设置多个加载事件。:

3:执行时机:

传统方式加载事件,是全部内容(文字、图片、样式)在浏览器显示完毕再给执行加载事件。

jquery方式加载事件,只要全部内容(文字、图片、样式)在内存里边对应的DOM树结构绘制完毕就给执行,有可能对应的内容在浏览器里边还没有显示。

什么是jquery jquery的工作原理

jquery是javascript的一个类库,说到底jquery就是javascript jquery主要是用来简化javascript的各种操作以及解决各种浏览器之间的兼容性 用jquery能办到的用javascript都能办到。

jQuery 是如何判断HTML页面加载完毕的?它的原理是什么?

jquery是一个轻量级的JS框架,这点相信大部分人都听过,而jquery之所以有这样一个称呼,就是因为它悄悄披了一件外衣,将自己给隐藏了起来。

//以下截取自jquery源码片段(function( window, undefined ) {   /*    源码内容    */})( window );

上面这一小段代码来自于1.9.0当中jquery的源码,它是一个无污染的JS插件的标准写法,专业名词叫闭包。可以把它简单的看做是一个函数,与普通函数不同的是,这个函数没有名字,而且会立即执行,就像下面这样,会直接弹出字符串。

(function( window, undefined ) {

alert("Hello World!");

})( window );

可以看出来这样写的直接效果,就相当于我们直接弹出一个字符串。但是不同的是,我们将里面的变量变成了局域变量,这不仅可以提高运行速度,更重要的是我们在引用jquery的JS文件时,不会因为jquery当中的变量太多,而与其它的JS框架的变量命名产生冲突。对于这一点,我们拿以下这一小段代码来说明。

var temp = "Hello World!";

(function( window, undefined ) {       var temp = "ByeBye World!";

})( window );

alert(temp);

这段代码的运行结果是Hello而不是ByeBye,也就是说闭包中的变量声明没有污染到外面的全局变量,倘若我们去掉闭包,则最终的结果会是ByeBye,就像下面这样。

var temp = "Hello World!";//    (function( window, undefined ) {

var temp = "ByeBye World!";//    })( window );

alert(temp);

由此就可以看出来,jquery的外衣就是这一层闭包,它是很重要的一个内容,是编写JS框架必须知道的知识,它可以帮助我们隐藏我们的临时变量,降低污染。

jQuery选择器的工作原理和优化分析

每次申明一个jQuery对象的时候,返回的是jQuery.prototype.init

对象,很多人就会不明白,init明明是jQuery.fn的方法啊,实际上这里不是方法,而是init的构造函数,因为js的prototype对象可

以实现继承,加上js的对象只是引用不会是拷贝,new

jQuery,new

jQuery.fn和new

jQuery.fn.init的子对象是一样的,只是有没有执行到init的不同,这里就不讲原因了,等下一篇再讲为什么会是这样。

当我们使用选择器的时候$(selector,content),就会执行

init(selectot,content),我们看看inti中是怎样执行的:

复制代码

代码如下:

if

(

typeof

selector

==

"string"

)

{

//正则匹配,看是不是HTML代码或者是#id

var

match

=

quickExpr.exec(

selector

);

//没有作为待查找的

DOM

元素集、文档或

jQuery

对象。

//selector是#id的形式

if

(

match

(match[1]

||

!context)

)

{

//

HANDLE:

$(html)

-

$(array)

//HTML代码,调用clean补全HTML代码

if

(

match[1]

){

selector

=

jQuery.clean(

[

match[1]

],

context

);

}

//

是:

$("#id")

else

{

//判断id的Dom是不是加载完成

var

elem

=

document.getElementById(

match[3]

);

if

(

elem

){

if

(

elem.id

!=

match[3]

)

return

jQuery().find(

selector

);

return

jQuery(

elem

);//执行完毕return

}

selector

=

[];

}

//非id的形式.在context中或者是全文查找

}

else{

return

jQuery(

context

).find(

selector

);

}

}

这里就说明只有选择器写成$(‘#id')的时候最快,相当于执行了一次

getElementById,后边的程序就不用再执行了。当然往往我们需要的选择器并不是这么简单,比如我们需要id下的CSS为className,

有这样的写法$(‘#id.className')和$(‘#id').find(‘.className');这两种写法的执行结果都是一样的,比

如div

id=”id”span

class=”className”/span/div,返回的肯定都是span

class=”className”/span,但是执行的效率是完全不一样的。

在分析一下上边的代码,如果不是$(‘#id')这样的简单选择器的话,都会执行find函

数,那我们再看看find到底是做用的:

复制代码

代码如下:

find:

function(

selector

)

{

//在当前的对象中查找

var

elems

=

jQuery.map(this,

function(elem){

return

jQuery.find(

selector,

elem

);

});

//下边的代码可以忽略,只是做一些处理

//这里应用了js的正则对象的静态方法test

//indexOf("..")需要了解一下xpath的语法,就是判断selector中包含父节点的写法

//本意就是过滤数组的重复元素

return

this.pushStack(

/[^+]

[^+]/.test(

selector

)

||

selector.indexOf("..")

-1

?

jQuery.unique(

elems

)

:

elems

);

}

如果这样写$(‘#id

.className'),就会执行到扩展的find(‘#id

.className',document),因为当前的this是document的jQuery数组,那我们在看看扩展的find他的实现,代码比较

多,就不列出来,总之就是从第二个参数传递进行的dom第一个子节点开始找,遇见#比对id,遇见.比对ClassName,还有:+-等处理。

那我们要优化,是不是就要想办法让第二个参数context的范围最小,那样遍历是不是就很少了?

如果我们这样写$(‘#id').find(‘.className'),那程序只这样执行

的,第一次init的时候执行一步getElementById,就return了,接着执行

find(‘.className',divDocument),divDocument就是我们第一次选择的是div标签,如果document下有很

多dom对象的时候,这次只遍历divDocument是不是少了很多次,而且在第一次选择id的速度也要比遍历快的多。

现在大家应该是明白了吧。就是说第一层选择最好是ID,而是简单选择器,目的就是定义范围,

提高速度,这次就说这些,选择写法的优化,其他的优化,下次再说。

那些年一直用jQuery处理事件,这些年想了解下内部原理吗

说起jQuery的事件,不得不提一下Dean Edwards大神 addEvent库,很多流行的类库的基本思想从他那儿借来的

jQuery的事件处理机制吸取了JavaScript专家Dean Edwards编写的事件处理函数的精华,使得jQuery处理事件绑定的时候相当的可靠。

在预留退路(graceful degradation),循序渐进以及非入侵式编程思想方面,jQuery也做的非常不错

事件的流程图

总的来说对于JQuery的事件绑定

在绑定的时候做了包装处理

在执行的时候有过滤器处理

.on( events [, selector ] [, data ], handler(eventObject) )

events:事件名

selector : 一个选择器字符串,用于过滤出被选中的元素中能触发事件的后代元素

data :当一个事件被触发时,要传递给事件处理函数的

handler:事件被触发时,执行的函数

例如:

var body = $('body')

body.on('click','p',function(){

console.log(this)

})

用on方法给body上绑定一个click事件,冒泡到p元素的时候才出发回调函数

这里大家需要明确一点:每次在body上点击其实都会触发事件,但是只目标为p元素的情况下才会触发回调handler

通过源码不难发现,on方法实质只完成一些参数调整的工作,而实际负责事件绑定的是其内部jQuery.event.add方法

on: function( types, selector, data, fn, /*INTERNAL*/ one ) {

var origFn, type;

// Types can be a map of types/handlers

if ( typeof types === "object" ) {

// ( types-Object, selector, data )

if ( typeof selector !== "string" ) {

// ( types-Object, data )

data = data || selector;

selector = undefined;

}

for ( type in types ) {

this.on( type, selector, data, types[ type ], one );

}

return this;

}

if ( data == null fn == null ) {

// ( types, fn )

fn = selector;

data = selector = undefined;

} else if ( fn == null ) {

if ( typeof selector === "string" ) {

// ( types, selector, fn )

fn = data;

data = undefined;

} else {

// ( types, data, fn )

fn = data;

data = selector;

selector = undefined;

}

}

if ( fn === false ) {

fn = returnFalse;

} else if ( !fn ) {

return this;

}

if ( one === 1 ) {

origFn = fn;

fn = function( event ) {

// Can use an empty set, since event contains the info

jQuery().off( event );

return origFn.apply( this, arguments );

};

// Use same guid so caller can remove using origFn

fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );

}

return this.each( function() {

jQuery.event.add( this, types, fn, data, selector );

});

针对事件处理,我们可以拆分2部分:

一个事件预绑定期

一个事件执行期

本章着重讲解事件的预绑定的时候做了那些处理,为什么要这样处理?

事件底层的绑定接口无非就是用addEventListener处理的,所以我们直接定位到addEventListener下面

jQuery.event.add 中有

elem: 目标元素

type: 事件类型,如’click’

eventHandle: 事件句柄,也就是事件回调处理的内容了

false: 冒泡

现在我们把之前的案例给套一下看看

var body = document.getElementsByTagName('body')

var eventHandle = function(){

console.log(this)

}

body .addEventListener( 'click’, eventHandle, false );

明显有问题,每次在body上都触发了回调,少了个p元素的处理,当然这样的效果也无法处理

eventHandle源码

回到内部绑定的事件句柄eventHandle ,可想而知eventHandle不仅仅只是只是充当一个回调函数的角色,而是一个实现了EventListener接口的对象

if ( !(eventHandle = elemData.handle) ) {

eventHandle = elemData.handle = function( e ) {

// Discard the second event of a jQuery.event.trigger() and

// when an event is called after a page has unloaded

return typeof jQuery !== core_strundefined (!e || jQuery.event.triggered !== e.type) ?

jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :

undefined;

};

// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events

eventHandle.elem = elem;

}

可见在eventHandle中并没有直接处理回调函数,而是映射到jQuery.event.dispatch分派事件处理函数了

仅仅只是传入eventHandle.elem,arguments , 就是body元素 与事件对象

那么这里有个问题,事件回调的句柄并没有传递过去,后面的代码如何关联?

本章的一些地方可能要结合后面的dispatch处理才能理清,但是我们还是先看看做了那些处理

on内部的实现机制

我们开从头来理清下jQuery.event.add代码结构,适当的跳过这个环节中不能理解的代码,具体遇到在提出

之前就提到过jQuery从1.2.3版本引入数据缓存系统,贯穿内部,为整个体系服务,事件体系也引入了这个缓存机制

所以jQuery并没有将事件处理函数直接绑定到DOM元素上,而是通过$.data存储在缓存$.cahce上

第一步:获取数据缓存

//获取数据缓存

elemData = data_priv.get( elem );

在$.cahce缓存中获取存储的事件句柄对象,如果没就新建elemData

第二步:创建编号

if ( !handler.guid ) {

handler.guid = jQuery.guid++;

}

为每一个事件的句柄给一个标示,添加ID的目的是 用来寻找或者删除handler,因为这个东东是缓存在缓存对象上的,没有直接跟元素节点发生关联

第三步:分解事件名与句柄

if ( !(events = elemData.events) ) {

events = elemData.events= {};

}

if ( !(eventHandle = elemData.handle) ) {

eventHandle = elemData.handle = function( e ) {

// Discard the second event of a jQuery.event.trigger() and

// when an event is called after a page has unloaded

return typeof jQuery !== core_strundefined (!e || jQuery.event.triggered !== e.type) ?

jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :

undefined;

};

eventHandle.elem = elem;

}

events,eventHandle 都是elemData缓存对象内部的,可见

在elemData中有两个重要的属性,

一个是events,是jQuery内部维护的事件列队

一个是handle,是实际绑定到elem中的事件处理函数

之后的代码无非就是对这2个对象的筛选,分组,填充了

第四步: 填充事件名与事件句柄

// Handle multiple events separated by a space

// jQuery(...).bind("mouseover mouseout", fn);

// 事件可能是通过空格键分隔的字符串,所以将其变成字符串数组

// core_rnotwhite:/\S+/g

types = ( types || "" ).match( core_rnotwhite ) || [""];

// 例如:'.a .b .c'.match(/\S+/g) → [".a", ".b", ".c"]

// 事件的个数

t = types.length;

while ( t-- ) {

// 尝试取出事件的命名空间

// 如"mouseover.a.b" → ["mouseover.a.b", "mouseover", "a.b"]

tmp = rtypenamespace.exec( types[t] ) || [];

// 取出事件类型,如mouseover

type = origType = tmp[1];

// 取出事件命名空间,如a.b,并根据"."分隔成数组

namespaces = ( tmp[2] || "" ).split( "." ).sort();

// There *must* be a type, no attaching namespace-only handlers

if ( !type ) {

continue;

}

// If event changes its type, use the special event handlers for the changed type

// 事件是否会改变当前状态,如果会则使用特殊事件

special = jQuery.event.special[ type ] || {};

// If selector defined, determine special event api type, otherwise given type

// 根据是否已定义selector,决定使用哪个特殊事件api,如果没有非特殊事件,则用type

type = ( selector ? special.delegateType : special.bindType ) || type;

// Update special based on newly reset type

// type状态发生改变,重新定义特殊事件

special = jQuery.event.special[ type ] || {};

// handleObj is passed to all event handlers

// 这里把handleObj叫做事件处理对象,扩展一些来着handleObjIn的属性

handleObj = jQuery.extend({

type: type,

origType: origType,

data: data,

handler: handler,

guid: handler.guid,

selector: selector,

needsContext: selector jQuery.expr.match.needsContext.test( selector ),

namespace: namespaces.join(".")

}, handleObjIn );

// Init the event handler queue if we're the first

// 初始化事件处理列队,如果是第一次使用,将执行语句

if ( !(handlers = events[ type ]) ) {

handlers = events[ type ] = [];

handlers.delegateCount = 0;

// Only use addEventListener if the special events handler returns false

// 如果获取特殊事件监听方法失败,则使用addEventListener进行添加事件

if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {

if ( elem.addEventListener ) {

elem.addEventListener( type, eventHandle, false );

}

}

}

// 特殊事件使用add处理

if ( special.add ) {

special.add.call( elem, handleObj );

// 设置事件处理函数的ID

if ( !handleObj.handler.guid ) {

handleObj.handler.guid = handler.guid;

}

}

// Add to the element's handler list, delegates in front

// 将事件处理对象推入处理列表,姑且定义为事件处理对象包

if ( selector ) {

handlers.splice( handlers.delegateCount++, 0, handleObj );

} else {

handlers.push( handleObj );

}

// Keep track of which events have ever been used, for event optimization

// 表示事件曾经使用过,用于事件优化

jQuery.event.global[ type ] = true;

}

// Nullify elem to prevent memory leaks in IE

// 设置为null避免IE中循环引用导致的内存泄露

elem = null;

},

这段比较长了分解下,最终的目的就是为填充events,eventHandle


网页名称:jquery原理解析,简述jquery的实现原理
当前网址:http://csdahua.cn/article/dsceogo.html
扫二维码与项目经理沟通

我们在微信上24小时期待你的声音

解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流