zoukankan      html  css  js  c++  java
  • jQuery源码分析

    函数可以实现类

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

    1、jQuery的无new构建

    jQuery没有使用new运行符将jQuery显示的实例化

    var aQuery = function(selector, context) {
           return new aQuery();
    }
    aQuery.prototype = {
        name:function(){},
        age:function(){}
    }

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

    在javascript中实例this只跟原型有关系,那么可以把jQuery类当作一个工厂方法来创建实例,把这个方法放到jQuery.prototye原型中

    var aQuery = function(selector, context) {
           return  aQuery.prototype.init();
    }
    aQuery.prototype = {
        init:function(){
            return this;
        }
        name:function(){},
        age:function(){}
    }

    很明显aQuery()返回的是aQuery类的实例,那么在init中的this其实也是指向的aQuery类的实例。问题来了init的this指向的是aQuery类,如果把init函数也当作一个构造器,那么内部的this要如何处理?
    var aQuery = function(selector, context) {
           return  aQuery.prototype.init();
    }
    aQuery.prototype = {
        init: function() {
            this.age = 18
            return this;
        },
        name: function() {},
        age: 20
    }
    aQuery().age  //18

    这样的情况下就出错了,因为this只是指向aQuery类的,所以需要设计出独立的作用域才行
    2、 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类原型上的属性与方法,实现的关键点
    // Give the init function the jQuery prototype for later instantiation
    jQuery.fn.init.prototype = jQuery.fn;

    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

    3、链式调用

    所返回的都是同一个对象,可以提高代码的效率。通过简单扩展原型方法并通过return this的形式来实现跨浏览器的链式调用。利用JS下的简单工厂模式,来将所有对于同一个DOM对象的操作指定同一个实例。 
    aQuery().init().name()
    
    // 分解
    a = aQuery();
    a.init()
    a.name()

    实现链式的基本条件就是实例this的存在,返回当前实例的this,从而又可以访问自己的原型了

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

    最糟糕的是所有对象的方法返回的都是对象本身,也就是说没有返回值,这不一定在任何环境下都适合。 Javascript是无阻塞语言,所以他不是没阻塞,而是不能阻塞,所以他需要通过事件来驱动,异步来完成一些本需要阻塞进程的操作,这样处理只是同步链式,异步链式jquery从1.5开始就引入了Promise,jQuery.Deferred。

    4、插件借口

    jQuery支持自己扩展属性,这个对外提供了一个接口,jQuery.fn.extend()来对对象增加方法,不会破坏jQuery的原型结构。从jQuery的源码中可以看到,jQuery.extend和jQuery.fn.extend其实是同指向同一方法的不同引用
    jQuery.extend = jQuery.fn.extend = function() {}

    jQuery.extend 调用的时候,this是指向jQuery对象的(jQuery是函数,也是对象!),所以这里扩展在jQuery上。
    而jQuery.fn.extend 调用的时候,this指向fn对象,jQuery.fn 和jQuery.prototype指向同一对象,扩展fn就是扩展jQuery.prototype原型对象。
    这里增加的是原型方法,也就是对象方法了。所以jQuery的api中提供了以上2中扩展函数。
    5、自调用匿名函数
    (function( window, undefined ) {
        // jquery code
    })(window);

    1、在第一个括号内,创建一个匿名函数;第二个括号,立即执行
    2、通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。这点非常有用也是一个JS框架必须支持的功能,必须确保jQuery创建的变量不能和导入他的程序所使用的变量发生冲突。
    3.     匿名函数从语法上叫函数直接量,JavaScript语法需要包围匿名函数的括号,事实上自调用匿名函数有两种写法(注意标红了的右括号):

    4、通过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window;这还不是关键所在,更重要的是,将window作为参数传入,可以在压缩代码时进行优化
    (function(a,b){})(window); // window 被优化为 a 
    5、参数列表中增加undefined呢? 在 自调用匿名函数 的作用域内,确保undefined是真的未定义。因为undefined能够被重写,赋予新的值。
    6、为了更好的兼容性和健壮性,请在每行代码后加上分号并养成习惯。
    (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.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对象上。

    参考:
  • 相关阅读:
    [o] SQLite数据库报错: Invalid column C
    startActivityForResult和setResult详解
    [o] duplicate column name: _id 问题解决
    [O] SQLite数据库报错:no such column
    [原创] SQLite数据库使用清单(下)
    [原创] SQLite数据库使用清单(上)
    iOS CocoaPods详解之 Analyzing dependencies
    iOS UILabe的详细使用及特殊效果
    一个简单的socket通信小demo
    iOS Lable给文字添加中划线和下划线
  • 原文地址:https://www.cnblogs.com/chenlogin/p/5121540.html
Copyright © 2011-2022 走看看