jQuery
是继prototype
以后又一个优秀的Javascript框架。它是轻量级的js库 ,它兼容CSS3
,还兼容各类浏览器(IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+)
,jQuery2.0
及后续版本将再也不支持IE6/7/8
浏览器。jQuery使用户能更方便地处理HTML(标准通用标记语言下的一个应用)、events、实现动画效果,而且方便地为网站提供AJAX交互
。jQuery还有一个比较大的优点是,它的文档说明很全,并且各类应用也说得很详细,同时还有许多成熟的插件可供选择。jQuery可以使用户的html页面保持代码和html内容分离,也就是说,不用再在html里面插入一堆js来调用命令了,只需定义id便可。jquery大体能够分为 DOM 、 ajax 、选择器 、 事件 、 动画。固然jquery有13个模块之多,这里的5个,是从另一个角度划分的。javascript
(令: jquery从2.0以后就不兼容IE 6/7/8 了)html
1、 整体架构
;(function(global, factory) { // 传入window能够将其做为一个局部变量使用,缩短了做用域链,大大加快执行速度 factory(global); }(typeof window !== "undefined" ? window : this, function(window, noGlobal) { // jquery方法 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); }; // jQuery.fn 是 jquery对象的原型对象 jQuery.fn = jQuery.prototype = {}; // 核心方法 // 回调系统 // 异步队列 // 数据缓存 // 队列操做 // 选择器引 // 属性操做 // 节点遍历 // 文档处理 // 样式操做 // 属性操做 // 事件体系 // AJAX交互 // 动画引擎 if ( typeof noGlobal === strundefined ) { window.jQuery = window.$ = jQuery; } return jQuery; }));
关于上述代码,解释以下:java
jQuery使用()将匿名函数括起来,而后后面再加一对小括号(包含参数列表)的目的,简单来讲就是小括号括起来后,就看成是一个表达式来处理,获得的就是一个
function
对象了。同时小括号也取得了这个函数的引用位置,而后传入参数就能够直接执行了。
总结: 全局变量是魔鬼, 匿名函数能够有效的保证在页面上写入JavaScript,而不会形成全局变量的污染,经过小括号,让其加载的时候当即初始化,这样就造成了一个单例模式的效果从而只会执行一次。
(function( global, factory ) { // 由于jquery既要再普通环境下运行,也要再例如AMD、commonJs下运行,因此咱们须要作不一样的适应 // node CommonJs规范 if ( typeof module === "object" && typeof module.exports === "object" ) { module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { // AMD factory( global ); } // 传入参数(window和一个执行函数) }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { // 【1】 // 建立jQuery对象, 其实是jQuery.fn.init所返回的对象 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); // 若是调用new jQuery, 生成的jQuery会被丢弃,最后返回jQuery.fn.init对象 // 所以能够直接调用jQuery(selector, context), 不须要使用new // 若是使用new jQuery()容易出现死循环 // 咱们日常使用 $() ,就是直接调用jquery函数了 } // 【2】 // 建立jQuery对象原型,为jQuery添加各类方法 jQuery.fn = jQuery.prototype = { ... } // 【3】 // 在调用new jQuery.fn.init后, jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype // 至关于将全部jQuery.fn的方法都挂载到一开始jQuery函数返回的对象上 // 这里就是jquery的一个独特之处了,很是的巧妙 jQuery.fn.init.prototype = jQuery.fn; // 【4】 // 建立jQuery.extend方法 jQuery.extend = jQuery.fn.extend = function() {、 ... } // 【5】 // 使用jQuery.extend扩展静态方法 jQuery.extend({}); // 【6】 // 为window全局变量添加$对象,在给window全局添加变量的时候颇有可可能会致使变量命名冲突哦,咱们以后会学习到如何处理这种命名冲突的解决方法 if ( typeof noGlobal === strundefined ) { // var strundefined = typeof undefined window.jQuery = window.$ = jQuery; // $('') // 同 jQuery('') } return jQuery; }));
2、 jQuery的类数组对象
能够这么理解,jquery主要的任务就是获取DOM
。操做DOM
。node
jQuery
的入口都是经过$()
来的,经过传入不一样的参数,实现了9种重载方法。jquery
1. jQuery([selector,[context]]) 2. jQuery(element) 3. jQuery(elementArray) 4. jQuery(object) 5. jQuery(jQuery object) 6. jQuery(html,[ownerDocument]) 7. jQuery(html,[attributes]) 8. jQuery() 9. jQuery(callback) // 9种用法总体来讲能够分三大块:选择器、dom的处理、dom加载。
例子喽ajax
经过$(".Class")构建的对象结构以下所示:\数组
<div> <span class="span">1</span> <span class="span">2</span> <span class="span">3</span> </div> console.log($('.span')); // jQuery.fn.init(3) [span.span, span.span, span.span, prevObject: jQuery.fn.init(1), context: document, selector: ".span"] // 0:span.span // 1:span.span // 2:span.span // context: document // length: 3 // prevObject: jQuery.fn.init [document, context: document] // selector:".span" // __proto__:Object(0)
// 模拟一下 function Ajquery(selecter) { // 若是传入的不是一个对象,则将其转换为对象 if(!(selecter instanceof Ajquery)) { return new Ajquery(selecter); } var elem = document.getElementById(/[^#].*/.exec(selector)[0]); // 获取id this[0] = elem; this.length = 1; this.context = document; this.selector = selector; this.get = function(num) { return this[num]; } return this; } // 使用 $('#show2').append(Ajquery('#book').get(0)); // 所以 $('')获取到的就是一个类数组对象
jQuery的无new构造原理promise
咱们在构造jQuery对象的时候,并无使用new来建立,但实际上是在jQuery
方法的内部,咱们使用了new
,这样就保证了当前对象内部就又了一个this对象,而且吧全部的属性和方法的键值对都映射到this上了,因此既能够经过链式取值,也能够经过索引取值。jquery除了实现了类数组结构
, 方法的原型共享
,还实现了静态和实例的共享
.浏览器
javascript就是函数式语言
,函数能够实现类,因此javascript不是一个严格的面向对象的语言。缓存
function ajquery(name){ this.name = name; } ajquery.prototype = function(){ say: function(){ return this.name; } } var a = new ajquery(); a.say();
$().ready() $().noConflict()
var aQuery = function(selector, context) { return new aQuery(); // 直接new一下 } aQuery.prototype = { name:function(){}, age:function(){} } // 若是是上诉的样子,直接new aQuery()则会致使死循环。
// 下面就是jquery的写法了 jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }, // 可是问题又来了,init中的this指向的是实例init的原型,就导师了jquery类的this分离了, // 解决这个问题的方法是: jQuery.fn.init.prototype = jQuery.fn;
以上就是jQuery无new构造的原理了
// 精简分析 var ajquery = function(name) { return new ajquery.prototype.init(name); } ajquery.prototype = { init: function(name) { this.name = name; return this; }, get: function() { return this.name; }, name: 'zjj' } ajquery.prototype.init.prototype = ajquery.prototype;//这里使得init内部的this跟ajquery类的this保持了一致。 console.log(ajquery('zmf').get()); // zmf
3、 ready和load事件
针对于文档的加载
// 一 $(function() { }) // 二 $(document).ready(function() { }) // 三 $(document).load(function() { })
在上面咱们看到了一个是ready
一个是load
,那么这两个有什么区别呢?
// 咱们先来看一个写DOM文档的加载过程吧 1. html 解析 2. 加载外部引用脚本和外部样式 3. 解析执行脚本 4. 构造DOM原型 // ready 5. 加载图片等文件 6. 页面加载完毕 // load
document.addEventListener("DOMContentLoaded", function () { console.log('DOMContentLoaded回调') }, false); // 当初始的 HTML 文档被彻底加载和解析完成以后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。 window.addEventListener("load", function () { console.log('load事件回调') }, false); console.log('脚本解析一') //测试加载 $(function () { console.log('脚本解析二') }) console.log('脚本解析三') // 观察脚本加载的顺序 // test.html:34 脚本解析一 // test.html:41 脚本解析三 // test.html:38 脚本解析二 // test.html:26 DOMContentLoaded回调 // test.html:30 load事件回调
看完上面的过程咱们不难看出ready是在文档加载完毕也就是DOM建立完毕后执行的,而load则是在页面加载完毕以后才执行的。
两者惟一的区别就是中间加了一个图片的加载,,可是图片等外部文件的加载确实很慢的呢。
在平时种咱们为了增长用户的体验效果,首先应该执行的是咱们的处理框架的加载。而不是图片等外部文件的加载。咱们应该越早处理DOM越好,咱们不须要等到图片资源都加载后才去处理框架的加载,这样就能增长用户的体验了。
// 源码分析 jQuery.ready.promise = function( obj ) { if ( !readyList ) { readyList = jQuery.Deferred(); if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready ); } else { document.addEventListener( "DOMContentLoaded", completed, false ); window.addEventListener( "load", completed, false ); } } return readyList.promise( obj ); };
DOM文档是否加载完毕处理方法
当HTML文档内容加载完毕后触发,并不会等待图像、外部引用文件、样式表等的彻底加载。
<script> document.addEventListener("DOMContentLoaded", function(event) { console.log("DOM fully loaded and parsed"); }); for(var i=0; i<1000000000; i++){ // 这个同步脚本将延迟DOM的解析。 // 因此DOMContentLoaded事件稍后将启动。 } </script>
该事件的浏览器支持状况是在IE9及以上支持。
兼容不支持该事件的浏览器
在IE8中可以使用readystatechange来检测DOM文档是否加载完毕。
对于跟早的IE,能够经过每隔一段时间执行一次document.documentElement.doScroll("left")
来检测这一状态,由于这条代码在DOM加载完毕以前执行时会抛出错误(throw an error)。
document.onreadystatechange = subSomething;//当页面加载状态改变的时候执行这个方法. function subSomething() { if(document.readyState == "complete"){ //当页面加载状态为彻底结束时进入 //你要作的操做。 } } // 用这个能够作一下等待网站图片或者其余东西加载完之后的操做,好比加载时咱们能够调用加载动画,当complete也就是加载完成时咱们让加载动画隐藏,这样只是一个小例子。仍是很完美的。
针对IE的加载检测
Diego Perini 在 2007 年的时候,报告了一种检测 IE 是否加载完成的方式,使用 doScroll 方法调用,详情可见http://javascript.nwbox.com/I...。
原理就是对于 IE 在非 iframe 内时,只有不断地经过可否执行 doScroll 判断 DOM 是否加载完毕。在上述中间隔 50 毫秒尝试去执行 doScroll,注意,因为页面没有加载完成的时候,调用 doScroll 会致使异常,因此使用了 try -catch 来捕获异常。
结论:因此总的来讲当页面 DOM 未加载完成时,调用 doScroll 方法时,会产生异常。那么咱们反过来用,若是不异常,那么就是页面DOM加载完毕了。
// Ensure firing before onload, maybe late but safe also for iframes document.attachEvent( "onreadystatechange", completed ); // A fallback to window.onload, that will always work window.attachEvent( "onload", completed ); // If IE and not a frame // continually check to see if the document is ready var top = false; try { // 非iframe中 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) { // 每一个50ms执行一次 return setTimeout( doScrollCheck, 50 ); } // 分离全部dom就绪事件 detach(); // and execute any waiting functions jQuery.ready(); } })(); }
3、 解决$的冲突
解决方案–– noConflict函数
。
引入jQuery运行这个noConflict
函数将变量$的控制权让给第一个实现它的那个库,确保jQuery不会与其余库的$对象发生冲突。
在运行这个函数后,就只能使用jQuery
变量访问jQuery对象
。例如,在要用到$("aaron")
的地方,就必须换成jQuery("aaron")
,由于$的控制权已经让出去了。
// jquery导入 jQuery.noConflict(); // 使用 jQuery jQuery("aaron").show(); // 使用其余库的 $() // 别的库导入 $("aaron").style.display = ‘block’;
这个函数必须在你导入jQuery文件以后,而且在导入另外一个致使冲突的库以前使用。固然也应当在其余冲突的库被使用以前,除非jQuery是最后一个导入的。
(function(window, undefined) { var // Map over jQuery in case of overwrite // 设置别名,经过两个私有变量映射了 window 环境下的 jQuery 和 $ 两个对象,以防止变量被强行覆盖 _jQuery = window.jQuery, _$ = window.$; jQuery.extend({ // noConflict() 方法让出变量 $ 的 jQuery 控制权,这样其余脚本就可使用它了 // 经过全名替代简写的方式来使用 jQuery // deep -- 布尔值,指示是否容许完全将 jQuery 变量还原(移交 $ 引用的同时是否移交 jQuery 对象自己) noConflict: function(deep) { // 判断全局 $ 变量是否等于 jQuery 变量 // 若是等于,则从新还原全局变量 $ 为 jQuery 运行以前的变量(存储在内部变量 _$ 中) if (window.$ === jQuery) { // 此时 jQuery 别名 $ 失效 window.$ = _$; } // 当开启深度冲突处理而且全局变量 jQuery 等于内部 jQuery,则把全局 jQuery 还原成以前的情况 if (deep && window.jQuery === jQuery) { // 若是 deep 为 true,此时 jQuery 失效 window.jQuery = _jQuery; } // 这里返回的是 jQuery 库内部的 jQuery 构造函数(new jQuery.fn.init()) // 像使用 $ 同样尽情使用它吧 return jQuery; } }) }(window)
使用实例:
<script src="prototype.js"></script>//1.包含jQuery以外的库(好比Prototype) <script src="jquery.js"></script>//2.包含jQuery库取得对$的使用权 <script> jQuery.noConflict();//3.调用noConflict()方法,让出$,把控制权让给最早包含的库 </script> <script src="myscript.js"></script>
让出$控制权后,须要使用jQuery方法时,则不能用$来调用了,要用jQuery。或者经过定义新的名称来代替$符号。
var jq=jQuery.noConflict();
另外还有一个技巧,能够再.ready()方法中使用$。它的回调函数能够接收一个参数,这个参数为jQuery对象自己,能够从新命名jQuery为$,这样也是不会形成冲突的。
jQuery.(document).ready(function($){ //这里能够正常使用$ })
jQuery源码学习系列