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

    一:前言

      主要是将jQuery里的实现框架提取出来了,常用用法都提取了一个例子,这里是$.ajax和$("#id").html(..)的例子,这里就直接上源码了,然后代码里加了自己的理解总结;

    如果想了解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>
  • 相关阅读:
    解决JDBC连接MySQL 8时得异常:java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone.
    洛谷P2604(最大流+最小费用最大流)
    poj2411(状压dp)
    二轮前水题计划
    最近挖的坑
    关于我
    future
    mysql学习笔记
    vue踩坑记
    XSS漏洞学习笔记
  • 原文地址:https://www.cnblogs.com/silentdoer/p/8931465.html
Copyright © 2011-2022 走看看