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

ArthursL ArthursL 2019-08-22

一、导读

文章作为学习笔记的形式,记录学习的一点体会和原理知识,如有错误欢迎指正。
本文根据一些简单的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下加载框架,如果是
//在一个固定的作用域内,能保证局部使用。

相关推荐