jQuery源码解析

一:前言

  主要是将jQuery里的实现方式提取出来了,每种用法都提取了一部分,自己不擅长长篇大论就直接上源码了,然后代码里就已经加了自己的注释;

这里主要实现了$.ajax(..)和$("#id").html(..)的功能,并且将jQuery的框架结构提取出来了,如果想了解jQuery实现原理的朋友可以参考看看;注意用ajax时跨域的问题,否则status老是0;

二:代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello world</title>
</head>
<script>
    // 这里省略很多的判断,只是做一个jQuery的实现演示
    (function (global, factory) {
        factory(global);  // TODO 引用此js脚本后立刻会执行的方法
    })(window, function(global, noGlobal){
        // TODO 此匿名方法是引入脚本后真正的实现逻辑所在;
        var
            version = "1.0",  // jQuery版本

            // TODO 重要部分,注意在jQuery(..)之前内部的jQuery.fn.init是可以的,只要在执行jQuery(..)时这些定义已经存在即可;
            jQuery = function (selector, context) {
                // TODO function了内部的实现可以先不考虑是否满足条件,因为它要在调用时浏览器才会判断这些定义是否存在,只需要在调用前做好这些工作即可
                return new jQuery.fn.init(selector, context);
            };  // 结束var

        // 这句代码的执行肯定先于jQuery(..),因此上面的jQuery.fn不会报undefined
        // 通过JSON对象直接一次性给jQuery赋“实例”属性
        jQuery.fn = jQuery.prototype = {
            jquery:version,
            constructor:jQuery,
            length:0  // 后面还有很多jQuery的“实例方法”定义,这里只是做demo就不演示了,感兴趣的可以自己看jQuery源码
        }

        // TODO 通过后面执行extend方法来为jQuery声明“静态成员方法“如ajax
        jQuery.extend = jQuery.fn.extend = function () {
            // 这里暂且认为调用extend时的参数都是单个json对象或没有参数,target要么等于json对象要么是“空json对象”
            var options = arguments[0] || {},  // TODO 通过arguments来获取调用extend方法传来的参数
                target = this,  //TODO 就是jQuery类/方法对象或jQuery.fn(通过jQuery.extend(..)是定义静态成员,jQuery.fn.extend(..)是实例成员
                name, copy;

            for(name in options){
                copy = options[name];  // TODO 可能需要添加到jQuery类的“静态方法或变量”
                if(target === copy){
                    continue;
                }
                if(copy !== undefined) {
                    target[name] = copy;  //TODO 将调用extend方法中JSON对象里的如ajax:function...的ajax作为“静态成员方法”赋值给jQuery
                }
            }
        }

        var rootjQuery,
            idSelectorExpr = /^(?:#([\w-]+))$/,
            init = jQuery.fn.init = function( selector, context, root ) {  // TODO 用jQuery时一般不会主动用到后面两个参数,这里暂且不管
                var elem, match;
                // 处理如: $(""), $(null), $(undefined)【包括了$()】, $(false)
                if ( !selector ) {
                    console.log("不合法的调用方式");
                    return this;
                }

                if(typeof selector === "string"){  // TODO $("#id")/$(":type")等都会来这里,这里先只做$("#id")的情况
                    // 通过pattern来匹配selector,然后match就是匹配到的字符串,其中match[0]就是group(0),若第一个子表达式没有匹配到则match[1]为null
                    match = idSelectorExpr.exec(selector);  // $("#ids")得到match[1]为ids
                }
                if(match && !context){
                    elem = document.getElementById(match[1]);  // 不用管match[1]是否有效
                    if(elem){
                        this[0] = elem;
                        this.length = 1;
                    }
                    return this;  // 返回new jQuery.fn.init(...)对象;
                }
            }

        init.prototype = jQuery.fn;  // TODO 对fn的添加也会应用到init.prototype上,jQuery.fn.init某种程度上说就等价于jQuery

        // TODO 执行jQuery.extend方法来给jQuery定义静态成员
        jQuery.extend({
            ajax:function (url, options) {  // 这里暂且规定用的时候就是$.ajax({..});
                if(typeof url === "object"){  // TODO 说明$.ajax(..)时只传了一个参数且为JSON对象
                    options = url;
                    url = undefined;
                }
                if(typeof url === "string" && typeof options === "undefined"){
                    console.error("不合法的使用");
                }
                options = options || {};  // TODO 防止只有一个参数且为null
                // TODO 这里对ajax做简单实现,注意这个url和引用js或css时的路径一样当前路径就是当前网页的url地址,而根路径则是ip:port/
                // JSON对象支持的属性:type/url/data/dataType/success/error/async/timeout/headers/beforeSend
                // TODO 这里暂且只支持XMLHttpRequest,type暂且只支持GET和POST
                var type = options["type"], url = options["url"], data = options["data"], dataType = options["dataType"],
                    success = options["success"], error = options["error"], async = options["async"], timeout = options["timeout"],
                    headers = options["headers"];
                // TODO 进行一个简单的校验
                if(type === "get" || type === "GET"){
                    data = null;
                }
                async = async || false;

                var xhr = new XMLHttpRequest();
                xhr.open(type, url, async);  // 通过请求行数据建立HTTP连接

                if(dataType === undefined){ // TODO 设置header
                    if(type === "post" || type === "POST"){
                        xhr.setRequestHeader("Content-Type", "application/octet-stream");
                    }
                }else{
                    headers.dataType = dataType;
                }
                headers = headers || {};

                for(var name in headers){
                    xhr.setRequestHeader(name, headers[name]);
                }

                // 每次变化时都会触发
                xhr.onreadystatechange = function () {
                    if(xhr.readyState === 4){  // TODO 4表示request请求是完成状态(包括200/500/404等等)
                        if(timer){  // timer只要在执行这个方法之前生成即可;
                            clearTimeout(timer);  // js原生方法,此时已经完成
                        }
                        if(xhr.status === 200){  // 暂定认为只有200时才是success
                            if(success){
                                success(xhr.responseText);  // 暂定success的参数只能是响应体数据
                            }
                        }else if(xhr.status >= 400 && xhr.status < 600){  // 暂定这个区间的状态码认为是error
                            if(error){
                                error();  // 暂定error没有参数
                            }
                        }
                    }
                }

                // TODO 设置超时时间
                var timer;
                if(typeof timeout === "number" && timeout > 0){
                    timer = setTimeout(function () {  // setTimeout是js里的原生方法
                        if(xhr) {
                            xhr.abort();  // 据说会重置readyState为0,当是暂不清楚时候也会触发onreadystatechange事件
                        }
                        alert("HTTP请求超时");
                    }, timeout);
                }
                xhr.send(data);  // TODO 通过继续发送请求头和请求体执行完整的HTTP请求
            }
        });

        // TODO 通过jQuery.fn.extend(..)给jQuery定义实例成员(注意jQuery.fn最终会赋值给jQuery.fn.init.prototype,这里实际上是给init对象创建实例成员
        jQuery.fn.extend({
            html:function (value) {
                var elem = this[0] || {},  // elem是通过$("#id")获得的元素
                    l = this.length;  // TODO 这个this就是 new jQuery.fn.init(..)产生的init类对象
                if(l < 1){
                    console.log("有bug");
                }
                if(value === undefined){
                    return elem.innerHTML;
                }else{
                    elem.innerHTML = value;
                }
            }
        });

        // TODO 使得外部可以直接$("#id")或$.ajax(..)
        if(!noGlobal){
            window.jQuery = window.$ = jQuery;
        }
    });
</script>
<body>
<h1>Index page</h1>
<p id="hh">uuuuu</p>

<button onclick="(function () {
      $('#hh').html('ii999');
    })()">调用修改$("id").html()</button>

<button onclick="(function () {
      $.ajax({
        url:'http://localhost:8090/test.html',
        type:'GET',
        async:true,
        success:function(data){
            var msg = 'silentdoer' + data;
            alert(msg);
        }
      });
    })()">调用ajax</button>
</body>
</html>

猜你喜欢

转载自www.cnblogs.com/silentdoer/p/8931465.html