从jQuery核心源码学习设计思想和JS特性

55次阅读

共计 3122 个字符,预计需要花费 8 分钟才能阅读完成。

一、导读

 文章作为学习笔记的形式,记录学习的一点体会和原理知识,如有错误欢迎指正。本文根据一些简单的 jQuery 源码入手分析一个框架从哪方面入手,js 在底层做了那些事,了解他的设计思想,jquery 整体架构还是依托于 js,在 js 基础上封装自己的方法,在解
读设计过程中更容易了解 js 的一些原理。

二、知识点列举

  1. 匿名自执行函数和闭合包。
  2. 原型
  3. 数据类型检测
  4. this 指向
  5. 绑定执行环境(call)
  6. 深拷贝

三、源码分析

//1. 匿名自执行函数和闭合包 这里形成闭包保护变量不被直接访问和篡改保证框架完整性,// 闭包的作用域内也会帮助缓存变量值。(function(root){
    // 这里其实是需要了解实例化一个构造函数时实际内容是他的 prototype
    var jQuery=function(){return new jQuery.prototype.init();
    }
    jQuery.fn=jQuery.prototype={init:function(){ },
        toString:Object.prototype.toString,// 需要借助 Object 原型上的 toString
        hasOwn:Object.prototype.hasOwnProperty,
        typeClass:{//toString 返回当前检测对象的属性以对象键的形式返回
            '[object Boolean]' : 'boolean', 

            '[object Number]' : 'number', 

            '[object String]' : 'string', 

            '[object Function]' : 'function', 

            '[object Array]' : 'array', 

            '[object Date]' : 'date', 

            '[object RegExp]' : 'regExp', 

            '[object Object]' : 'object' 
        },
        // 这里 call 方法把 Object 原型上的 toString 的执行环境替换到 obj 下,因为对象的 
        //prototype 上的 toString 方法才会返回变量类型,// 如果根据原型链的规律,函数或数组实例化时的会有自己的 toString;
        isObject:function(obj){return this.typeClass[this.toString.call(obj)]=='object';
        },
        isFunction(fun){return this.typeClass[this.toString.call(fun)]=='function';
        },
        type :function(obj) { // 返回值类型

            return obj == null ? String(obj) : this.typeClass[toString.call(obj)] || "object"; 
            
        }, 
        isWindow : function(obj) { // 判断是不是 window
            
            return obj && typeof obj === "object" && "setInterval" in obj; 
            
        }, 
        // 先看支不支持 isArray 方法 不支持才调用本身的 type 检测
        isArray : Array.isArray || function(obj) {return this.type(obj) === "array"; 
            
        }, 
        isPlainObject : function(obj) { 
            // 排除不能深拷贝的类型 nodeType 不能是 dom 元素,不能是 window 对象
            if (!obj || this.type(obj) !== "object" || obj.nodeType || this.isWindow(obj)) {return false;} 
        
            if (obj.constructor && !this.hasOwn.call(obj, "constructor") 
            
                && !this.hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { // 判断如果有构造函数,是不是继承而来的继承来的太深了不能复制
            
                    return false; 
            
                }
                var key; 
                // 判断属性是不是自身的,如果原型上继承来的属性 那这
                //
                for (key in obj) { } 
            
                   return key === undefined || this.hasOwn.call(obj, key); 
        }
        
    }
    /** 
     * 思考为什么入参一定要是对象;* 多参数时候  对象是引用类型,当成参数传入的时候我们改变了入参的值,也就改变了原对象所指向地址的值
     * 单个参数组件拓展时候传入的是方法,方法作为对象的属性也成了引用类型这样插入的组件指向传入方法的地址
     * 
     * */ 
    jQuery.fn.extend=jQuery.extend=function(){var target=arguments[0]||{};
        var option,key,deep,src, copy, copyIsArray, clone,
            length=arguments.length,
            deep = false,// 默认浅拷贝
            i=1;// 决定循环第几个入参
        if(typeof target==="boolean"){// 判断是否传入深拷贝
            deep=target;
            target=arguments[1]||{};// 传的深拷贝开关 参数往后移一位
            i=2;// 第三个入参开始是要被合并的对象
        }
        if(!jQuery.fn.isObject(target) && !jQuery.fn.isFunction(target)){target = {};
        };
        if(length === i){// 只传入一个参数时候
            target=this;// 指向调用 extend 的对象,也就是 jquery 的实例对象 jquery.prototype 或 jqery
            i--;// 让下面循环将方法加入当前 this 指向的对象,傻吊老师没写
        };
        for(;i<length;i++){   // 浅层拷贝
            if((option=arguments[i])){for(key in option){src=target[key];
                    copy=option[key];
                    if (target === copy) {// 目标对象等于被拷贝对象的一个属性,可能会出现对象内部的循环引用???continue;
                    }
                    if (deep && copy && ( jQuery.fn.isPlainObject(copy) || (copyIsArray = jQuery.fn.isArray(copy)) ) ) {if ( copyIsArray) {// 如果是数组的话 以数字下标为 key 拷贝数组
                          copyIsArray = false;
                          clone = src && jQuery.fn.isArray(src) ? src : [];} else {clone = src && jQuery.fn.isPlainObject(src) ? src : {};}
                       target[key] = jQuery.fn.extend(deep, clone, copy);// 递归调用
                     } else if (copy !== undefined) {target[ key] = copy;
                  }
                }
            }
        }
        return target;
    }

    jQuery.fn.init.prototype=jQuery.fn;
    // 共享原型对象,在实例化 jQuery.prototype.init 的时候实际上引用了 jQuery.prototype 的属性
    // 在调用自己实例化自己的属性时候会陷入死循环,共享原型可以用自己原型的一个属性共享自己的原型。root.$=root.jQuery=jQuery;// 属性挂载到全局

})(this)//this 指向执行当前函数的对象,如果在 window 下就会在 window 下加载框架,如果是
// 在一个固定的作用域内,能保证局部使用。

正文完
 0