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

    如果说php是世界上最好的语言,那么javascript无疑可以称为世界上最飘逸的语言,最近看了下jQuery的源码,实现了一个简陋的jQuery。我觉得要看懂jQuery整体结构,需要搞懂js作用域链,闭包,js prototype继承,关于闭包网络上的定义实在太多了,这里参照了js权威指南里的定义,感觉从js的角度好理解一点。

    闭包:js函数对象不仅包含函数的代码逻辑,还引用了当前的作用域链,

    函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内

    这种特性在计算机科学文献中称为闭包,所有的js函数都是闭包。

    javascript运行在它被定义的作用域里,而不是执行的作用域里

    关于js作用域参见 : http://www.laruence.com/2009/05/28/863.html

    废话少说,上滥代码

    <!DOCTYPE html>
    <html>
    <head>
        <title>jQuery源码浅析</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <script>
    /**
     * 
     * 参照jQuery 3.2.1源码
     * 省略了一些规范,如AMD, Commonjs
     * 整个jQuery包含在匿名函数中,函数就是闭包
     */
    
    (function(window, factory){
        
        factory(window);
    
    })(window, function(window){
        /**
         * 为什么要传window对象?
         * 1.在jQuery里可以把window对象当局部变量使用
         * 2.压缩的时候window变量名可以压缩至1个字节
         */
        
        var getProto = Object.getPrototypeOf;
        var class2type = {};
        var toString = class2type.toString;
        var hasOwn = class2type.hasOwnProperty;
        var fnToString = hasOwn.toString;
        var ObjectFunctionString = fnToString.call( Object );
        
        /**
         * 工厂方法,返回jQuery.fn.init的实例,即jQuery对象
         * selector待查找的字符串,源码里还有context参数,此处省略
         * 我们所说的jQuery其实准确的说是jQuery工厂方法,调用jQuery工厂方法返回的才是真正的jQuery对象
         */
        var jQuery = function(selector){
                return new jQuery.fn.init(selector);
            },
            version = "3.2.1";
        
        //jQuery原型对象赋值给jQuery.fn,定义在jQuery.prototype里的方法可以被所有的jQuery对象使用
        jQuery.fn = jQuery.prototype = {
            
            jquery: version,
    
            constructor: jQuery,
            
            //...省掉了一些方法
        };
        
        
        
        //jQuery.fn的属性init函数,jQuery的选择器使用了Ssize,这里简单的使用一个id选择器 
        jQuery.fn.init = function(selector){
            /* (在Javascript中,This关键字永远都指向函数(方法)的所有者) this指向的是jQuery.fn
            *  这里简单的将DOM对象赋值给this[0],其他属性省略, 我们使用jQuery的时候使用下标0即可将jQuery对象转化为普通的DOM对象
            */
            this[0] = window.document.getElementById(selector);
            return this;
        };
        
        
        /**
        * 这一句很关键
        * 将jQuery.fn赋值给jQuery.fn.init的原型,这样jQuery.fn.init的实例(通常我们所说的jQuery对象就是它)可以使用jQuery.fn的方法
        * 结合之前可以得出 jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype
        * jQuery.fn,jQuery.prototype扩展的方法属性 jQuery对象可以使用
        */
        jQuery.fn.init.prototype = jQuery.fn;
        
        //实现了jQuery的html方法
        jQuery.fn.html = function(value){
            if(typeof value === "string"){
                this[0].innerHTML = value;
                return this;
            }else{
                return this[0].innerHTML;
            }
        };
        
        
        /**
         * 
         * jQuery扩展方法,除了可以扩展jQuery外,还可以扩展你指定的对象
         * jQuery.extend 扩展jQuery,可以理解为扩展类方法
         * jQuery.fn.extend 扩展jQuery.fn,即jQuery实例可以使用,可以理解为扩展实例方法
         * 
         * 具体用法
         * 1.jQuery.extend(obj) 扩展jQeury
         * 2.jQuery.extend(true, obj) 深度扩展jQuery
         * 3.jQuery.extend(obj1, obj2) 扩展obj1
         * 4.jQuery.extend(true obj1, obj2) 深度扩展obj1
         */
        jQuery.extend = jQuery.fn.extend = function() {
            var options, name, src, copy, copyIsArray, clone,
                target = arguments[ 0 ] || {},
                i = 1,
                length = arguments.length,
                deep = false;
    
            //第一个参数为boolean且为true的时候为深度扩展
            if ( typeof target === "boolean" ) {
                deep = target;
                // Skip the boolean and the target
                target = arguments[ i ] || {};
                i++;
            }
            
            //被扩展的对象不是对象或者函数
            if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
                target = {};
            }
    
            //参数只有1个对象的情况下,扩展jQuery,多个对象则扩展第一个对象
            if ( i === length ) {
                target = this;
                i--;
            }
    
            for ( ; i < length; i++ ) {
                //只处理非空值
                if ( ( options = arguments[ i ] ) != null ) {
    
                    // Extend the base object
                    for ( name in options ) {
                        src = target[ name ];
                        copy = options[ name ];
    
                        // Prevent never-ending loop
                        if ( target === copy ) {
                            continue;
                        }
    
                        //仅在属性为纯粹对象或者 数组的时候深度拷贝才有效
                        if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
                            ( copyIsArray = Array.isArray( copy ) ) ) ) {
    
                            if ( copyIsArray ) {
                                copyIsArray = false;
                                clone = src && Array.isArray( src ) ? src : [];
    
                            } else {
                                clone = src && jQuery.isPlainObject( src ) ? src : {};
                            }
    
                            //递归扩展
                            target[ name ] = jQuery.extend( deep, clone, copy );
    
                        // Don't bring in undefined values
                        } else if ( copy !== undefined ) {
                            target[ name ] = copy;
                        }
                    }
                }
            }
            
            // Return the modified object
            return target;
        };
        
        
        jQuery.extend( {
            
            isFunction: function( obj ) {
                return jQuery.type( obj ) === "function";
            },
            
            isPlainObject: function( obj ) {
                var proto, Ctor;
    
                // Detect obvious negatives
                // Use toString instead of jQuery.type to catch host objects
                if ( !obj || toString.call( obj ) !== "[object Object]" ) {
                    return false;
                }
    
                proto = getProto( obj );
    
                // Objects with no prototype (e.g., `Object.create( null )`) are plain
                if ( !proto ) {
                    return true;
                }
    
                // Objects with prototype are plain iff they were constructed by a global Object function
                Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
                return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
            },
            
            type: function( obj ) {
                if ( obj == null ) {
                    return obj + "";
                }
    
                // Support: Android <=2.3 only (functionish RegExp)
                return typeof obj === "object" || typeof obj === "function" ?
                class2type[ toString.call( obj ) ] || "object" :
                typeof obj;
            },
        });
    
        //简易ready方法实现, 只支持DOMContentLoaded
        jQuery.extend( {
            readyList : [],
            ready: function(fn){
                if(fn && typeof fn === 'function'){
                    //向数组末尾添加函数
                    jQuery.readyList.push(fn);
                }else{
                    //顺序执行ready绑定的方法
                    for(var i in jQuery.readyList){
                        fn = jQuery.readyList[i];
                        //fn来自于全局作用域,属于window对象
                        fn.call(window, jQuery);
                    }
                }
            }
        });
        
        //只支持DOMContentLoaded
        document.addEventListener( "DOMContentLoaded", completed );
        window.addEventListener( "load", completed );
        function completed() {
            document.removeEventListener( "DOMContentLoaded", completed );
            window.removeEventListener( "load", completed );
            jQuery.ready();
        }
        //只暴露了两个变量到全局作用域
        window.$ = window.jQuery =  jQuery;
    });
    
    $.ready(function(){
        console.log('----设置id为test的元素文档内容,并获取文档内容----' + $('test').html('jQuery').html());
    });
    
    $.ready(function(){
        console.log(1);
    });
    
    $.ready(function(){
        console.log(2);
    });
    
    $.ready(function(){
        console.log(3);
    });
    
    var obj1 = {
        ball : {
            nba : 'nba'
        },
    };
    
    var obj2 = {
        ball : {
            cba : 'cba'
        }
    };
    
    var obj3 = {
        ball : {
            nba : 'nba'
        }
    };
    
    //扩展obj1
    $.extend(obj1, obj2);
    /**
     * {ball : {'cba' : 'cba'}}
     */
    
    //深度扩展obj3
    $.extend(true, obj3, obj2);
    /**
     * {ball : {'nba' : 'nba'}, {'cba' : 'cba'}}
     */
    </script>
    <body>
        <div id="test">my jQuery</div>
    </body>
    </html>
  • 相关阅读:
    Dapper数据库字段和model属性映射
    SQLServer性能调优之缓存
    python&django 实现页面中关联查询小功能(基础篇)
    pythonのdjango select_related 和 prefetch_related()
    pythonのdjango 在控制台用log打印操作日志
    pythonのdjango Form简单应用。
    pythonのdjango 信号
    pythonのdjango 缓存
    pythonのdjango CSRF简单使用
    pythonのdjango Session体验
  • 原文地址:https://www.cnblogs.com/gaoqin31/p/7219049.html
Copyright © 2011-2022 走看看