最近想看一下jquery源码,搜到了这样一篇博客《从jquery源码学到的10件事情》
本文基于这篇视频博客,提炼了一些内容,分享给大家。
说明:
这篇文章写于2010年,作者在视频里使用的是jquery 1.4版本,我根据视频里讲到的内容,对应目前的1字头1.11版本做了一些调整,一些被抛弃或者被移除的内容页做了删减,并在此感谢原作者
黑箱/black box
黑箱系统的概念是给定输入返回输出的一个系统,黑箱把实现过程进行封装。这里说的jquery黑箱是为js全局变量window输出jquery 和 $,而过程被封装到黑箱里,与外界互不干扰。
jquery 1.4版本的黑箱是利用了类似如下的自执行函数
(function( window, undefined){})(window)
作者给了一个比较通用的实现黑箱的方法
undefined = true;
(function(window, document, undefined){
if(foo == undefined) {
}
})(this, document)
jquery的黑箱里多传了第三个形参叫做undefined,而传实参的时候并没有传值,js里没有传值的形参会被设置为undefined,保证了黑箱内部undefined的正确性。js中,undefined作为一个全局属性,是可以被赋值的,比如上述代码中的undefined = true;
以自执行函数的模式实现黑箱的另外一个好处是利于压缩,比如下述的情况,我们只需要在黑箱内部使用简单的变量。
(function(a, b, c)){
b.getelementbyid('')
})(this, document)
作者为匿名函数自执行举了很多例子,比如下面这个,为页面的某一部分不停地更新(以及不断地执行)
(function loop(){
dostuff();
$('#update').load('awesomething.php',function(){
loop();
})
//settimeout(loop, 100)
})()
jquery 1.11版本的黑箱采用了全新的工厂方法,本文不探究
noconflict的实现
这个函数的差异不大,1.11 版本代码如下
var
// map over jquery in case of overwrite
_jquery = window.jquery,
// map over the $ in case of overwrite
_$ = window.$;
jquery.noconflict = function( deep ) {
if ( window.$ === jquery ) {
window.$ = _$;
}
if ( deep && window.jquery === jquery ) {
window.jquery = _jquery;
}
return jquery;
};
我们可以看到防冲突的实现是先把之前的jquery 和 $ 存起来,noconflict被调用的时候,再还给它们
与原生js属性命名的转换
1.4版本用的是props对象来存放jquery对属性操作与原生js属性操作的对应关系
1.11版缩减版本是这样的
jquery.extend({
propfix: {
"for": "htmlfor",
"class": "classname"
},
prop: function( elem, name, value ) {
//...
name = jquery.propfix[ name ] || name;
},
prophooks: {
//...
}
});
jquery.each([
"tabindex",
"readonly",
"maxlength",
"cellspacing",
"cellpadding",
"rowspan",
"colspan",
"usemap",
"frameborder",
"contenteditable"
], function() {
jquery.propfix[ this.tolowercase() ] = this;
});
propfix 这个对象是存放对应关系表的,比如class转换成classname,prop函数负责处理这个关系表。
而下面的each很有意思,遍历数组中那些属性,然后把他们小写格式对应到自己,放到 propfix
特效speed
我们知道在jquery里一些动画我们可以直接通过normal,fast,slow 来定义实现速度,这个在源码里是这样定义的
jquery.fx.speeds = {
slow: 600,
fast: 200,
// default speed
_default: 400
};
调皮的原作者做了这样一些事情:
var isie
//...
jquery.fx.speeds._default = isie ? 800 : 400
jquery.fx.speeds.veryfast = 200;
$('...').fadein('veryfast')
一种是可以对default属性做条件判断,还有一种自定义速度,比如”veryfast”
.ready
ready函数 1.11版本和1.4版本有较大的差距,新版中很多东西我也不太能理解,我们就简单的把核心拿出来看一下
jquery.ready.promise = function( obj ) { //...省略若干 } else if ( document.addeventlistener ) { // 使用addeventlistener "domcontentloaded" 监听ready事件 document.addeventlistener( "domcontentloaded", completed, false ); // 备选方案 "load" window.addeventlistener( "load", completed, false ); //如果ie } else { // ensure firing before onload, maybe late but safe also for iframes //ie下 attachevent 的"onreadystatechange" document.attachevent( "onreadystatechange", completed ); // a fallback to window.onload, that will always work //备选方案onload window.attachevent( "onload", completed ); // if ie and not a frame // continually check to see if the document is ready var top = false; try { 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) { return settimeout( doscrollcheck, 50 ); } // detach all dom ready events detach(); // and execute any waiting functions jquery.ready(); } })(); } } } return readylist.promise( obj );};
.ready 利用了下面的.promise去做确保载入完成的工作,重点是document.addeventlistener( "domcontentloaded", completed, false );window.addeventlistener( "load", completed, false );document.attachevent( "onreadystatechange", completed );window.attachevent( "onload", completed );
兼容性考量的四种检查方式
其中从top开始,做了一件事情就是ie下面,dom节点判断是否有scroll,在ie下如果dom有scroll,没有scroll到的元素对ready会有影响,这里面我的理解不够,总之jquery里用到了一个叫做diego perini的技巧,可以在注释里的地址看到更多内容。
选择器
$('#id').find('tag.thing') --- faster
$('#id tag.thing') ------- using sizzle
原作者在这里说了一个jquery效率的问题,上面的方法更快一些,而下面的方法稍微慢,简单地说是因为下面的方法调用了sizzle,通过sizzle其实转换成上述的模式,而id的调用则是直接过jquery.init.
这里需要扩展一下,我们来看一下1.11里jquery对象究竟长啥样
jquery = function( selector, context ) {
// the jquery object is actually just the init constructor 'enhanced'
// need init if jquery is called (just allow error to be thrown if not included)
return new jquery.fn.init( selector, context );
}
jquery对象其实是return了一个它自己的构造函数叫做init,我们再来看一下init做了些什么
// initialize a jquery object
init = jquery.fn.init = function( selector, context ) {
var match, elem;
// handle: $(""), $(null), $(undefined), $(false)
//超级省略...下略
// handle html strings
// handle: $(html) -> $(array)
// handle: $(html, props)
// handle: $(#id)
// handle: $(expr, $(...))
// handle: $(expr, context)
// handle: $(domelement)
// handle: $(function)
return jquery.makearray( selector, this );
};
// give the init function the jquery prototype for later instantiation
init.prototype = jquery.fn;
从上面的摘取的代码注释中,我们可以看到jq自己的构造函数里处理了哪些情况,其中包括html标签名和id的获取,意味着这两种获取是最底层的,此外$()的其他处理都要经过其他的函数,效率上不如上述处理情况。
同时我们也能看到init的原型被赋予了jquery.fn, 关于jquery对象的相关内容,感兴趣的朋友可以再多去了解一些。
jq的状态选择符,比如:not,:has,:eq存放在
sizzle.selectors.pseudos里面
以上就是从jquery中应该明白哪些知识?的详细内容。