zoukankan      html  css  js  c++  java
  • jquery 源码笔记(一) 整体架构:

    1、使用了jquery,但是觉得了解 jquery的源码才能 更容易知道怎么使用,所以在网上找了一些 jquery的源码 笔记 还有看了 妙味课堂 的 一部分视频,现在写一些总结。

    一、  jquery的 总体架构:

    1、jquery 有良好的对外接口,  window.jQuery = window.$ = jQuery;

    现在 是 通过jquery 2.0.3 源码的分析:

      (21,94)  21—94行, 定义了一些变量和函数,   jQuery = function();

     (96,283)  96—283行,  给 jQuery对象,添加一些属性和方法

     (285,347) extend : jQ的继承方法,  类似 : $().css()  $().html()   css和html 是jquery实例的方法,

     (349,817)  jQuery.extend  扩展一些工具方法, $.trim()  $.proxy()   trim和proxy是全局方法,也称为工具方法,

    (877,2856) Sizzle  : 复杂选择器的实现,

    (2880,3042)Callbacks : 回调对象,对函数的统一管理

    (3043,3183)Deffered    延迟对象:对异步的统一管理

    (3184,3295)support:功能检测

    (3308,3652)data(): 数据缓存

     (3653,3797)  queue(): 队列管理

     (3803,4299)  attr()  prop() val() addClass() 等    对元素属性的操作,

    (4300,5128)  on()  trigger() :事件操作的相关方法,

     (5140,6057) DOM 操作, 添加(append  prepend) 删除(remove) 获取(next ) 包装(wrap)筛选(filter find)

    (6058,6620) CSS() :样式操作

    (6621,7854)提交的数据和Ajax(),  ajax()  load(),getJson()

     (7855,8584)  animate()  动画 运动的操作,

     (8585,8792) offset()   scrollTop():  位置和尺寸的方法,

     (8804,8821) jQuery 支持模块化的模式

     (8826)  window.jquery= window.$ = jQuery; 对外提供的接口,

    要创建这样一个“自调用匿名函数”呢?

    通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。这点非常有用也是一个JS框架必须支持的功能,jQuery被应用在成千上万的JavaScript程序中,必须确保jQuery创建的变量不能和导入他的程序所使用的变量发生冲突。

    (function(window,undefined){
    
          })(window)

    为什么要传入window呢?

    1、通过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window;2、将window作为参数传入,可以在压缩代码时进行优化

    在参数列表中增加undefined呢?

    在 自调用匿名函数 的作用域内,确保undefined是真的未定义。因为undefined能够被重写,赋予新的值。

    分析一:jQuery的无new构建

    $().find().css()
    $().hide().html('....').hide().

    从上面的写法上至少可以发现2个问题

    1. jQuery对象的构建方式

    2 .jQuery方法的调用方式

    常规的使用方法:

     var aQuery = function(){
                  //构造函数
          }
    
          aQuery.prototype = {
    
                 name: function(),
                 age:function()
          } 
    
          var a = new aQuery();
          a.name();

    但是jquery并不是这样的,  $().css();

    要实现这样,那么jQuery就要看成一个类,那么$()应该是返回类的实例才对,   所以把代码改一下:

    var aQuery = function(){
                  //构造函数
                  return new aQuery();
          }
    
          aQuery.prototype = {
                 name: function(),
                 age:function()
          } 

    通过new aQuery(),虽然返回的是一个实例,但是也能看出很明显的问题,死循环了!


    那么如何返回一个正确的实例?

    在javascript中实例this只跟原型有关系

    那么可以把jQuery类当作一个工厂方法来创建实例,把这个方法放到jQuery.prototye原型中

    var aQuery = function(){
                  //构造函数
                  return aQuery.prototype.init();
          }
    
          aQuery.prototype = {
    
                 init:function(){
                         return this;
                 },
                 name:function(),
                 age:function()
          } 

    当执行aQuery() 返回的实例:

    image

    很明显aQuery()返回的是aQuery类的实例,那么在init中的this其实也是指向的aQuery类的实例

    问题来了init的this指向的是aQuery类,如果把init函数也当作一个构造器,那么内部的this要如何处理?

     var aQuery = function(){
                  //构造函数
                  return aQuery.prototype.init();
          }
    
          aQuery.prototype = {
    
                 init:function(){
                         this.age = 18;
                         return this;
                 },
                 name:function(){},
                 age:20
          } 
          alert(aQuery().age);     //18

    这样的情况下就出错了,因为this只是指向aQuery类的,所以需要设计出独立的作用域才行,


    jQuery框架分隔作用域的处理

    jQuery = function( selector, context ) {
            // The jQuery object is actually just the init constructor 'enhanced'
            return new jQuery.fn.init( selector, context, rootjQuery );
        },

    很明显通过实例init函数,每次都构建新的init实例对象,来分隔this,避免交互混淆

    那么既然都不是同一个对象那么肯定又出现一个新的问题

    例如:

    复制代码
    var aQuery = function(selector, context) {
           return  new aQuery.prototype.init();
    }
    aQuery.prototype = {
        init: function() {
            this.age = 18
            return this;
        },
        name: function() {},
        age: 20
    }
    
    //Uncaught TypeError: Object [object Object] has no method 'name' 
    console.log(aQuery().name())
    复制代码

    抛出错误,无法找到这个方法,所以很明显new的init跟jquery类的this分离了


    怎么访问jQuery类原型上的属性与方法?

         做到既能隔离作用域还能使用jQuery原型对象的作用域呢,还能在返回实例中访问jQuery的原型对象?

    实现的关键点

    // Give the init function the jQuery prototype for later instantiation
    jQuery.fn.init.prototype = jQuery.fn;

    通过原型传递解决问题,把jQuery的原型传递给jQuery.prototype.init.prototype

    换句话说jQuery的原型对象覆盖了init构造器的原型对象

    因为是引用传递所以不需要担心这个循环引用的性能问题

    复制代码
    var aQuery = function(selector, context) {
           return  new aQuery.prototype.init();
    }
    aQuery.prototype = {
        init: function() {
            return this;
        },
        name: function() {
            return this.age
        },
        age: 20
    }
    
    aQuery.prototype.init.prototype = aQuery.prototype;
    
    console.log(aQuery().name()) //20
    • 通过new jQuery.fn.init() 构建一个新的对象,拥有init构造器的prototype原型对象的方法
    • 通过改变prorotype指针的指向,让这个新的对象也指向了jQuery类的原型prototype
    • 所以这样构建出来的对象就继续了jQuery.fn原型定义的所有方法了

    源码结构

    (function( window, undefined ) {
       
        var jQuery = (function() {
           // 构建jQuery对象
           var jQuery = function( selector, context ) {
               return new jQuery.fn.init( selector, context, rootjQuery );
           }
       
           // jQuery对象原型
           jQuery.fn = jQuery.prototype = {
               constructor: jQuery,
               init: function( selector, context, rootjQuery ) {
                  // selector有以下7种分支情况:
                  // DOM元素
                  // body(优化)
                  // 字符串:HTML标签、HTML字符串、#id、选择器表达式
                  // 函数(作为ready回调函数)
                  // 最后返回伪数组
               }
           };
       
           // Give the init function the jQuery prototype for later instantiation
           jQuery.fn.init.prototype = jQuery.fn;
       
           // 合并内容到第一个参数中,后续大部分功能都通过该函数扩展
           // 通过jQuery.fn.extend扩展的函数,大部分都会调用通过jQuery.extend扩展的同名函数
           jQuery.extend = jQuery.fn.extend = function() {};
          
           // 在jQuery上扩展静态方法
           jQuery.extend({
               // ready bindReady
               // isPlainObject isEmptyObject
               // parseJSON parseXML
               // globalEval
               // each makeArray inArray merge grep map
               // proxy
               // access
               // uaMatch
               // sub
               // browser
           });
     
            // 到这里,jQuery对象构造完成,后边的代码都是对jQuery或jQuery对象的扩展
           return jQuery;
       
        })();
       
        window.jQuery = window.$ = jQuery;
    })(window);

    jQuery对象不是通过 new jQuery 创建的,而是通过 new jQuery.fn.init 创建的

    jQuery对象就是jQuery.fn.init对象

      如果执行new jQeury(),生成的jQuery对象会被抛弃,最后返回 jQuery.fn.init对象;因此可以直接调用jQuery( selector, context ),没有必要使用new关键字

     先执行 jQuery.fn = jQuery.prototype,再执行 jQuery.fn.init.prototype = jQuery.fn,合并后的代码如下:

    jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype

    所有挂载到jQuery.fn的方法,相当于挂载到了jQuery.prototype,即挂载到了jQuery 函数上(一开始的 jQuery = function( selector, context ) ),但是最后都相当于挂载到了jQuery.fn.init.prototype,即相当于挂载到了一开始的jQuery 函数返回的对象上,即挂载到了我们最终使用的jQuery对象上。


    分析二:链式调用

    DOM链式调用的处理:

    1.节约JS代码.

    2.所返回的都是同一个对象,可以提高代码的效率

    通过简单扩展原型方法并通过return this的形式来实现跨浏览器的链式调用。

    利用JS下的简单工厂模式,来将所有对于同一个DOM对象的操作指定同一个实例。

    这个原理就超简单了

    aQuery().init().name()
    
    分解
    a = aQuery();
    a.init()
    a.name()

    把代码分解一下,很明显实现链式的基本条件就是实例this的存在,并且是同一个

    复制代码
    aQuery.prototype = {
        init: function() {
            return this;
        },
        name: function() {
            return this
        }
    }
    复制代码

    所以我们在需要链式的方法访问this就可以了,因为返回当前实例的this,从而又可以访问自己的原型了

    aQuery.init().name()

    优点:节省代码量,提高代码的效率,代码看起来更优雅

    最糟糕的是所有对象的方法返回的都是对象本身,也就是说没有返回值,这不一定在任何环境下都适合。


    Javascript是无阻塞语言,所以他不是没阻塞,而是不能阻塞,所以他需要通过事件来驱动,异步来完成一些本需要阻塞进程的操作,这样处理只是同步链式,异步链式jquery从1.5开始就引入了Promise,jQuery.Deferred后期在讨论。


    分析三:插件接口

    jQuery的主体框架就是这样,但是根据一般设计者的习惯,如果要为jQuery或者jQuery prototype添加属性方法,同样如果要提供给开发者对方法的扩展,从封装的角度讲是不是应该提供一个接口才对,字面就能看懂是对函数扩展,而不是看上去直接修改prototype.友好的用户接口,

    jQuery支持自己扩展属性,这个对外提供了一个接口,jQuery.fn.extend()来对对象增加方法

    从jQuery的源码中可以看到,jQuery.extend和jQuery.fn.extend其实是同指向同一方法的不同引用

    jQuery.extend = jQuery.fn.extend = function() {
    jQuery.extend 对jQuery本身的属性和方法进行了扩展
    
    jQuery.fn.extend 对jQuery.fn的属性和方法进行了扩展

    通过extend()函数可以方便快速的扩展功能,不会破坏jQuery的原型结构

    jQuery.extend = jQuery.fn.extend = function(){...}; 这个是连等,也就是2个指向同一个函数,怎么会实现不同的功能呢?这就是this 力量了!

    针对fn与jQuery其实是2个不同的对象,在之前有讲述:

    • jQuery.extend 调用的时候,this是指向jQuery对象的(jQuery是函数,也是对象!),所以这里扩展在jQuery上。
    • 而jQuery.fn.extend 调用的时候,this指向fn对象,jQuery.fn 和jQuery.prototype指向同一对象,扩展fn就是扩展jQuery.prototype原型对象。
    • 这里增加的是原型方法,也就是对象方法了。所以jQuery的api中提供了以上2中扩展函数。
  • 相关阅读:
    MVC3 string equlas int 方法
    AjAx ComponentArt. NavBar 的用法
    GridView重写排序、分页 (原作)
    如何用 Calendar 控件来做日程管理
    无刷新仿google波形扭曲彩色Asp.net验证码
    Asp.net 2.0图形报表制作chart(原作)
    WinForm.Net 界面皮肤使用资源(C#原作)
    java Date类用法(转)
    画类图
    LCA tarjan hdu 2586代码详细步骤(转)
  • 原文地址:https://www.cnblogs.com/a-lonely-wolf/p/5693481.html
Copyright © 2011-2022 走看看