您好,欢迎访问一九零五行业门户网

jquery源码的基本介绍

这篇文章主要介绍了关于jquery源码学习一之概况,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下
jqueryjquery是继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个,是从另外一个角度划分的。
(令: jquery从2.0之后就不兼容ie 6/7/8 了)
一、 总体架构;(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;}));
关于上述代码,解释如下:
jquery的整体代码包裹在一个立即执行的自调用匿名的函数中,这样可以尽量减少和其他第三方库的干扰;自动初始化这个函数,让其只构建一次。
在上述代码最后,将jquery对象添加到全局window上,并为之设置变量$,从而使得可以在外界访问jquery;
为自调用匿名函数设置参数global,并传入参数window,这样一方面可以缩短作用域链,可以尽快访问到window;
自调用函数的原理(立即执行的自调用函数)
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;    }));
二、 jquery的类数组对象可以这么理解,jquery主要的任务就是获取dom。操作dom。
jquery的入口都是通过$()来的,通过传入不同的参数,实现了9种重载方法。
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加载。
在jquery内部使用了一种类数组对象的形式,这也是为什么我们获取到同一个class的许多dom后,既能够调用jquery的方法,又能通过数组的方式来处理每一个dom对象了(比如遍历)。
例子喽
通过$(.class)构建的对象结构如下所示:\
<p>     <span class="span">1</span>    <span class="span">2</span>    <span class="span">3</span></p>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构造原理
我们在构造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();
但是在jquery中却不是这么来的。jquery没有使用new运行符将jquery显示的实例化,还是直接调用其函数
$().ready() $().noconflict()
如果要实现不用new直接获得实例
var aquery = function(selector, context) {       return new aquery(); // 直接new一下}aquery.prototype = {    name:function(){},    age:function(){}}// 如果是上诉的样子,直接new aquery()则会导致死循环。
如何得到一个正确的实例呢,那么可以把jquery类当作一个工厂方法来创建实例,把这个方法放到jquery.prototye原型中,然后实例化这个方法,从而创建一个实例
// 下面就是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
三、 ready和load事件针对于文档的加载
// 一$(function() {})// 二$(document).ready(function() {})// 三$(document).load(function() {})
在上面我们看到了一个是ready一个是load,那么这两个有什么区别呢?
// 我们先来看一个写dom文档的加载过程吧1. html 解析2. 加载外部引用脚本和外部样式3. 解析执行脚本4. 构造dom原型  // ready5. 加载图片等文件 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文档是否加载完毕处理方法
domcontentloaded
当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及以上支持。
兼容不支持该事件的浏览器readystatechange
在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 iframesdocument.attachevent( onreadystatechange, completed );// a fallback to window.onload, that will always workwindow.attachevent( onload, completed );// if ie and not a frame// continually check to see if the document is readyvar 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();        }    })();}
三、 解决$的冲突$太火热,jquery采用$作为命名空间,不免会与别的库框架或者插件相冲突。
解决方案–– noconflict函数。
引入jquery运行这个noconflict函数将变量$的控制权让给第一个实现它的那个库,确保jquery不会与其他库的$对象发生冲突。
在运行这个函数后,就只能使用jquery变量访问jquery对象。例如,在要用到$(aaron)的地方,就必须换成jquery(aaron),因为$的控制权已经让出去了。
// jquery导入jquery.noconflict();// 使用 jqueryjquery(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/vue的鼠标移入移出效果
jquery中css()和attr()方法的区别
以上就是jquery源码的基本介绍的详细内容。
其它类似信息

推荐信息