• 小菜的jquery2.1.1源码分析(一)


    自己根据自己的理解去尝试分析下大名鼎鼎的jquery的源码,一来提高自己使用jqueryAPI的使用能力,最重要的是提高自己javascript的能力,加油!

    下载的是官网的http://code.jquery.com/jquery-2.1.1.js,2.1.1版本。

    (function( global, factory ) {
    
        if ( typeof module === "object" && typeof module.exports === "object" ) {
            // For CommonJS and CommonJS-like environments where a proper window is present,
            // execute the factory and get jQuery
            // For environments that do not inherently posses a window with a document
            // (such as Node.js), expose a jQuery-making factory as module.exports
            // This accentuates the need for the creation of a real window
            // e.g. var jQuery = require("jquery")(window);
            // See ticket #14549 for more info
            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 {
            factory( global );
        }
    
    // Pass this if window is not defined yet
    }(typeof window !== "undefined" ? window : this, function( window, noGlobal )

    一.第一行先定义了一个匿名函数并且执行,这是很多库的标准用法,在一个匿名函数闭包中运行,例如jquery,匿名函数里面的变量不会与外界冲突,只在9182行调用

    window.jQuery = window.$ = jQuery;将jQuery和$挂载到window下,从而使用jQuery和$符来调用匿名函数中的各种方法和属性。

    二.接着进行判断    if ( typeof module === "object" && typeof module.exports === "object" )。根据我的理解,是在之前如果应用到了符合CommonJS规范的js库,例如

    Node.js,做一些冲突的协调,这个需要node.js等的一些知识。这个先不去理他。

    三.如果没引用那些库,直接调用factory( global );factory和global正是这个大的匿名函数(function( global, factory )的两个参数,看一下这个匿名函数传的实参

    (1)第一个参数window,首先判断  typeof window !== "undefined" ? window : this,如果window的类型不等于undefined,则传window参数,等于undefined,则传this,为什么

           这么用,因为window是可以改变的,到后面可能会用到

    (2)第二个参数是一个匿名函数function(window,noGlobal)。考虑到一般情况,function( global, factory ) {factiory(global)}(window,function...);这样这个匿名函数就容易理解了,        其实就是调用定义并且调用function(window,noGlobal),或者是 function(this,noGlobal)。之所以要这么用,因为这样会使作用域链变短,并且防止与其他库的冲突。

    四.旧版的jquery好像没有上面的这个功能,是直接调用的function(window,noGlobal),下面开始漫长之路了,开始分析jquery的主函数 function(window,noGlobal)。

    var arr = [];
    
    var slice = arr.slice;
    
    var concat = arr.concat;
    
    var push = arr.push;
    
    var indexOf = arr.indexOf;
    
    var class2type = {};
    
    var toString = class2type.toString;
    
    var hasOwn = class2type.hasOwnProperty;
    
    var support = {};
    
    
    
    var
        // Use the correct document accordingly with window argument (sandbox)
        document = window.document,
    
        version = "2.1.1",
    
        // Define a local copy of 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 );
        },
    
        // Support: Android<4.1
        // Make sure we trim BOM and NBSP
        rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
    
        // Matches dashed string for camelizing
        rmsPrefix = /^-ms-/,
        rdashAlpha = /-([\da-z])/gi,
    
        // Used by jQuery.camelCase as callback to replace()
        fcamelCase = function( all, letter ) {
            return letter.toUpperCase();
        };

    39~72行,定义了一些变量,在后面会用到,用到了再说

    73~77行,

    1 jQuery = function( selector, context ) {
    2         // The jQuery object is actually just the init constructor 'enhanced'
    3         // Need init if jQuery is called (just allow error to be thrown if not included)
    4         return new jQuery.fn.init( selector, context );
    5     }

    定义了最重要的jQuery符号,例如我们调用$("a")即jQuery("a")相当于创建了jquery的一个实例,可以调用该实例的方法和属性,即new jQuery.fn.init("a");

    78~86行,定义了一些正则表达式规则,在以后会用到。

    rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g  表示不同序列编码的空格开始或结尾

    rmsPrefix = /^-ms-/,以-ms-开头

    rdashAlpha = /-([\da-z])/gi  以-后跟数字字母的前缀并且不区分大小写,这是为了将那些用-连接的字符串弄成驼峰的形式。

    88~90行

    fcamelCase = function( all, letter ) {
    return letter.toUpperCase();
    };

    定义了一个简单的方法,就是返回参数的大写形式。

    五.jQuery的原型

     1 jQuery.fn = jQuery.prototype = {
     2     // The current version of jQuery being used
     3     jquery: version,
     4 
     5     constructor: jQuery,
     6 
     7     // Start with an empty selector
     8     selector: "",
     9 
    10     // The default length of a jQuery object is 0
    11     length: 0,
    12 
    13     toArray: function() {
    14         return slice.call( this );
    15     },
    16 
    17     // Get the Nth element in the matched element set OR
    18     // Get the whole matched element set as a clean array
    19     get: function( num ) {
    20         return num != null ?
    21 
    22             // Return just the one element from the set
    23             ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
    24 
    25             // Return all the elements in a clean array
    26             slice.call( this );
    27     },
    28 
    29     // Take an array of elements and push it onto the stack
    30     // (returning the new matched element set)
    31     pushStack: function( elems ) {
    32 
    33         // Build a new jQuery matched element set
    34         var ret = jQuery.merge( this.constructor(), elems );
    35 
    36         // Add the old object onto the stack (as a reference)
    37         ret.prevObject = this;
    38         ret.context = this.context;
    39 
    40         // Return the newly-formed element set
    41         return ret;
    42     },
    43 
    44     // Execute a callback for every element in the matched set.
    45     // (You can seed the arguments with an array of args, but this is
    46     // only used internally.)
    47     each: function( callback, args ) {
    48         return jQuery.each( this, callback, args );
    49     },
    50 
    51     map: function( callback ) {
    52         return this.pushStack( jQuery.map(this, function( elem, i ) {
    53             return callback.call( elem, i, elem );
    54         }));
    55     },
    56 
    57     slice: function() {
    58         return this.pushStack( slice.apply( this, arguments ) );
    59     },
    60 
    61     first: function() {
    62         return this.eq( 0 );
    63     },
    64 
    65     last: function() {
    66         return this.eq( -1 );
    67     },
    68 
    69     eq: function( i ) {
    70         var len = this.length,
    71             j = +i + ( i < 0 ? len : 0 );
    72         return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
    73     },
    74 
    75     end: function() {
    76         return this.prevObject || this.constructor(null);
    77     },
    78 
    79     // For internal use only.
    80     // Behaves like an Array's method, not like a jQuery method.
    81     push: push,
    82     sort: arr.sort,
    83     splice: arr.splice
    84 };

    这一片就是原型的属性和方法。jQuery.fn = jQuery.prototype是为了以后写的方便

    比如直接可以用$.jquery就是调用jQuery.jquery,返回的是version,即在之前定义的常量,juqery的版本号。

    93~103,定义了几个属性,比如version版本号,constructor构造器,selector选择器,length长度,因为等于说是初始化,都是空或者0。

    104~168,接下来是原型上的方法,挨个分析下,并且举出一些例子。

    后来发现jquery重写了slice方法,因此先来看slice方法

    slice: function() {
    return this.pushStack( slice.apply( this, arguments ) );
    }

    比较坑,又用到了pushStack方法,再来看

    pushStack: function( elems ) {
    
    // Build a new jQuery matched element set
    var ret = jQuery.merge( this.constructor(), elems );
    
    // Add the old object onto the stack (as a reference)
    ret.prevObject = this;
    ret.context = this.context;
    
    // Return the newly-formed element set
    return ret;
    }

    比较坑+1,用到了merge方法,继续找,在424行找到,先看下

    merge: function( first, second ) {
            var len = +second.length,
                j = 0,
                i = first.length;
    
            for ( ; j < len; j++ ) {
                first[ i++ ] = second[ j ];
            }
    
            first.length = i;
    
            return first;
        }

    函数比较好看懂,该方法传两个参数,参数应该都为数组,len为第二个数组长度,i为第一个数组的长度,从[0,len)开始循环,达到的效果是两个数组进行合并到第一个参数数组,然后

    再把第一个参数的长度设置成两个数组长度的和。返回第一个数组。

    例如$.merge( [0,1,2], [2,3,4] )得到[0, 1, 2, 2, 3, 4]

    往前捋,看pushStack方法

    toArray: function() {
            return slice.call( this );
        },

    就是类似于将字符串的slice方法应用,只不过是改了个名字罢了,一个是jquery对象上的方法,一个是字符串的方法而已。比如$("a").toArray(),就是将所有的匹配到的a标签组成的

    jquery对象集合转化为数组

    get: function( num ) {
            return num != null ?
    
                // Return just the one element from the set
                ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
    
                // Return all the elements in a clean array
                slice.call( this );
        },

    get方法,先判断参数num是否为null,如果是null,直接调用jquery的slice方法,即与toArray方法相同,如果不为null,再进行判断是否小于零,小于零,则从匹配到的jquery对象集合中返回

    倒数第-num个对象,如果大于等于零,则返回对象集合中第num+1个jquery对象。

    例如$("a").get(null)相当于调用$("a").toArray(),返回数组形式的jquery对象集合;

      $("a").get(1)返回jquery对象集合中第二个匹配到的jquery对象

      $("a").get(-1)返回jquery对象集合中倒数第一个匹配到的jquery对象。

    pushStack: function( elems ) {
    
            // Build a new jQuery matched element set
            var ret = jQuery.merge( this.constructor(), elems );
    
            // Add the old object onto the stack (as a reference)
            ret.prevObject = this;
            ret.context = this.context;
    
            // Return the newly-formed element set
            return ret;
        }

    pushStack方法这个里面调用了jQuery对象的merge方法,比较蛋疼,等看到了merge方法再补充。

  • 相关阅读:
    临床诊断与ICD10编码(3)肿瘤疾病编码
    临床诊断与ICD10编码(2)ICD10编码原则
    临床诊断与ICD10编码(1)前言
    webstrom中使用svn出现问题,无法连接url
    @RequestBody和@RequestParam的区别
    (转)使用Chrome://inspect调试 Android 设备上Webview
    (转)spring、spring boot、spring mvc之间的关系
    websotrom无法对使用了泛型的ts进行自动补全
    webpack里publicPath的配置
    博客网站
  • 原文地址:https://www.cnblogs.com/xiaoyouzi/p/3929166.html
Copyright © 2020-2023  润新知