专栏首页gaoqin31jQuery源码浅析

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>

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • c语言函数的隐式声明

    编译器只有碰到函数原型的时候才知道这个函数的名字,参数类型个数返回值,到函数调用的时候才知道如何生成指令,所以函数原型必须出现在函数调用之前。

    用户3094376
  • Linux系统下强制踢掉登录用户

    linux系统root用户可强制踢制其它登录用户,首先可用$w命令查看登录用户信息,显示信息如下:       [root@Wang ~]# w      10...

    用户3094376
  • 设计模式之 UML

    今年接手的一个计费项目让我痛苦不堪,里面到处充斥着重复冗余的代码,一个简单的需求往往需要改n个地方,而且很多改动牵一发动全身,这个项目涉及到支付,出问题就是损失...

    用户3094376
  • 理解jquery的$.extend & $.fn.extend用法

    jQuery为开发插件提拱了两个方法,分别是: 1、jQuery.fn.extend(); //dom方法 2、jQuery.extend(); // 工具...

    连胜
  • 十三、jQuery过时的今天,你还会使用它吗

    我至今还记得当初从jQuery源码中学到一星半点应用技巧的时候常会有一种发自内心的惊叹,“原来JavaScript居然可以这样用!”

    用户6901603
  • 【前端面试】字节跳动2019校招面经 - 前端开发岗(二)

    querySelectorAll()返回的是DOM原生element对象 而jQuery的选择器返回的是jQuery的包装对象,同时包含了原生DOM对象和一些j...

    CloudCat
  • 初识jQuery 基础篇

    首先提供一个知识点外资源: 在线编程:无需编程环境:   https://codepen.io/pen   添加jQuery库:     Settings-->...

    房上的猫
  • jquery 自定义函数方法

    * jQuery中添加自定义或函数方法1,如 $.fn.extend({'aa':function(){}}) 或 jQuery.fn.aa=function(...

    traffic
  • 检测是否IE

    从jQuery的jQuery.browser里面拿出来的,jQuery从1.9开始废弃这个方法,改用jQuery.support来检测浏览器支持的特性。

    libo1106
  • 腾讯云推出自研服务器星星海 综合性能提升35%

    10月29日,在腾讯全球数字生态大会成都峰会上,腾讯云正式宣布推出首款自研服务器星星海。该服务器是腾讯第一款真正为云而生的服务器,硬件系统采用模块化设计理念,并...

    用户2416682

扫码关注云+社区

领取腾讯云代金券