• jQuery的extend方法仅仅是字面意思上的扩展吗?


      jQuery中extend的使用方式大多是这样的:

        jQuery.extend({
    
            // Unique for each copy of jQuery on the page
            expando: "jQuery" + (version + Math.random()).replace(/\D/g, ""),
    
            // Assume jQuery is ready without the ready module
            isReady: true,
    
            error: function (msg) {
                throw new Error(msg);
            },
        })
    

      jQuery几乎在每一部分模块代码的下方,都会加上这样的一段代码,来添加操作该模块的属性和方法。

      如果去搜索jQuery.extend会发现,这个函数还可以这么用:

     var udataCur = jQuery.extend({}, udataOld);
     var anim = Animation(this, jQuery.extend({}, prop), optall);
    

      这样的使用方法,可不太像是添加扩展了。一个函数不只能实现一个功能?不如去看看,函数内部是怎么实现的,以及这个函数到底能用来干什么。

    .extend源码:

        jQuery.extend = jQuery.fn.extend = function () {
            var options, name, src, copy, copyIsArray, clone,
                target = arguments[0] || {},
                i = 1,
                length = arguments.length,
                deep = false;
    
            // Handle a deep copy situation
            if (typeof target === "boolean") {
                deep = target;
    
                // Skip the boolean and the target
                target = arguments[i] || {};
                i++;
            }
    
            // Handle case when target is a string or something (possible in deep copy)
            if (typeof target !== "object" && !isFunction(target)) {
                target = {};
            }
    
            // Extend jQuery itself if only one argument is passed
            if (i === length) {
                target = this;
                i--;
            }
    
            for (; i < length; i++) {
    
                // Only deal with non-null/undefined values
                if ((options = arguments[i]) != null) {
    
                    // Extend the base object
                    for (name in options) {
                        copy = options[name];
    
                        // Prevent Object.prototype pollution
                        // Prevent never-ending loop
                        if (name === "__proto__" || target === copy) {
                            debugger
                            continue;
                        }
    
                        // Recurse if we're merging plain objects or arrays
                        if (deep && copy && (jQuery.isPlainObject(copy) ||
                            (copyIsArray = Array.isArray(copy)))) {
                            src = target[name];
    
                            // Ensure proper type for the source value
                            if (copyIsArray && !Array.isArray(src)) {
                                clone = [];
                            } else if (!copyIsArray && !jQuery.isPlainObject(src)) {
                                clone = {};
                            } else {
                                // clone = src = 1
                                clone = src;
                            }
                            copyIsArray = false;
    
                            // Never move original objects, clone them
                            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;
        };
    

      好家伙,72行代码实现了一个函数,看看里面都写了点儿啥。

      首先,这个函数并没有指定入参,而是通过arguments加下标的形式,来动态取参数,从函数内部的注解中也能看出些端倪,注释中有提到 Handle a deep copy situation 即 处理深拷贝的场景,那么目前可知,这个函数,除了能为jQuery本身提供相应模块的扩展, 还可以对对象进行拷贝

       代码最开始部分定义了函数中用到的变量:

            var options, name, src, copy, copyIsArray, clone,
                // 传入的第一个参数,如果不存在,则默认为{}
                target = arguments[0] || {},
                i = 1,
                // 传参个数
                length = arguments.length,
                // 是否为深拷贝操作,默认为false
                deep = false;
    

       接下来是一些容错和函数应用何种场景的判断

            // 如果首个参数为boolean类型,则代表该函数用于对象的深拷贝
            if (typeof target === "boolean") {
                deep = target;
    
                // 直接取第二个参数,作为拷贝操作的目标对象
                target = arguments[i] || {};
                // 指针后移一位
                i++;
            }
    
            // 处理传参不符合要求的情况
            if (typeof target !== "object" && !isFunction(target)) {
                target = {};
            }
    
            // 如果只传递了一个参数,则视为扩展jQuery
            if (i === length) {
                target = this;
                i--;
            }w
    

       再往下,就是函数处理拷贝的相关操作了:

            for (; i < length; i++) {
    
                // 跳过为空的情况
                if ((options = arguments[i]) != null) {
    
                    // Extend the base object
                    for (name in options) {
                        copy = options[name];
                        //源码里说是防止污染原型链同时规避调用自身造成的死循环,但是
                        // 打上debugger后,这里并没有被执行过,可能是出于严谨考虑吧
                        if (name === "__proto__" || target === copy) {
                            continue;
                        }
    
                        // Recurse if we're merging plain objects or arrays
                        if (deep && copy && (jQuery.isPlainObject(copy) ||
                            (copyIsArray = Array.isArray(copy)))) {
                            src = target[name];
    
                            // Ensure proper type for the source value
                            if (copyIsArray && !Array.isArray(src)) {
                                clone = [];
                            } else if (!copyIsArray && !jQuery.isPlainObject(src)) {
                                clone = {};
                            } else {
                                clone = src;
                            }
                            copyIsArray = false;
    
                            // Never move original objects, clone them
                            target[name] = jQuery.extend(deep, clone, copy);
    
                            // Don't bring in undefined values
                        } else if (copy !== undefined) {
                            // 这里说一下,如果是扩展的应用场景,那么,之前已经将
                            // this指向了target,所以,这一步在扩展中就相当于
                            // jQuery[name] = copy
                            target[name] = copy;
                        }
                    }
                }
            }
    

       说一下有关深浅拷贝的概念:
        浅拷贝,只复制对象内部的基本类型数据,如果对象内部还有对象,则新旧对象共享同一个对象引用
        深拷贝,是完全复制一个新对象出来,两个对象间没有任何联系。
         写个例子对比一下:

    //浅拷贝
    let obj1 = {a: 1, b: {c: 2}};  
    let obj2 = Object.assign({}, obj1); 
    
    obj2.a = 'k'
    obj2.b.c = 3
    console.log(obj1) //{a: 1, b: {c: 3}}
    
    // obj1经由 Object.assign 生成了一个自身的浅拷贝对象 obj2
    // 当修改obj2的a属性时,原对象obj1的a属性不会受到影响,但是
    // 修改obj2的b.c属性时,obj1的b.c属性也相应的发生了变化。
    // 这就是浅拷贝只能复制基础属性,无法复制对象内引用的含义
    
    //深拷贝
        let obj1 = {a: 1, b: {c: 2}};
    
        let clone = function () {
            let src = arguments[0]
            let target = {}
            for (const name in src) {
                if (typeof src[name] === 'object') {
                    src = src[name]
                    target[name] = clone(src)
                } else {
                    target[name] = src[name]
                }
            }
    
            return target
        }
    
        let obj2 = clone(obj1)
        obj2.a = 'k'
        obj2.b.c = 3
        console.log(obj1) //{a: 1, b: {c: 2}}
        
    // 最终的运行结果,可以看到,修改深拷贝后的对象基础数据和引用
    // 都不会对原始数据产生影响
    

       了解深浅拷贝的概念后,继续回到源代码,在处理拷贝的开始,有一个for循环,执行条件为 i

        // 判断当前是否为深拷贝场景,同时参数需要满足条件:纯对象或数组
        if (deep && copy && (jQuery.isPlainObject(copy) ||
            (copyIsArray = Array.isArray(copy)))) {
            src = target[name];
    
            // Ensure proper type for the source value
            if (copyIsArray && !Array.isArray(src)) {
                clone = [];;
            } else if (!copyIsArray && !jQuery.isPlainObject(src)) {
                clone = {};
            } else {
                clone = src
            }
            copyIsArray = false;
    
            // Never move original objects, clone them
            // 递归处理,直到需要拷贝的属性不再是对象或数组
            target[name] = jQuery.extend(deep, clone, copy);
    
            // Don't bring in undefined values
        } else if (copy !== undefined) {
            target[name] = copy;
        }
    

       jQuery设计的这个extend函数,通过arguments动态获取参数以及对不同传参类型的判断,来实现了对扩展和拷贝这两种应用场景的重载,其实也不应该算两种情况,因为本身扩展就是一种拷贝行为。

       上面代码中有两个jQuery提供的工具函数,正好也看看这两个函数是如何实现的

    isFunction

        var isFunction = function isFunction(obj) {
    
            // Support: Chrome <=57, Firefox <=52
            // In some browsers, typeof returns "function" for HTML  elements
            // (i.e., `typeof document.createElement( "object" ) === "function"`).
            // We don't want to classify *any* DOM node as a function.
            // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5
            // Plus for old WebKit, typeof returns "function" for HTML collections
            // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756)
            return typeof obj === "function" && typeof obj.nodeType !== "number" &&
                typeof obj.item !== "function";
        };
    
    // jQuery判断传参是否为一个function类型的工具函数,在这里框架做了很多的兼容,例如
    // 在有些浏览器中  元素 会被当成一个function
    // 老旧版本的WebKit内核会将HTML元素集合当成function
     
    

    isPlainObject

            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;
            },
    
  • 相关阅读:
    基于javaweb的学籍管理系统计算机毕业论文java毕业设计选题源代码
    单链表的插入删除
    强化深度学习中使用Dyna-Q算法确定机器人问题中不同规划的学习和策略实战(超详细 附源码)
    ChatGPT科研绘图丨散点图、柱状图、小提琴图、箱型图、雷达图、玫瑰图、气泡图、森林图、三元图、三维图等各类科研图
    LEARN GIT
    Linux使用一个脚本启用、停用springboot项目(本文带脚本)
    数据结构—树、有序二叉树
    LeetCode热题100 【cpp】题解(一)哈希表和双指针
    php安装imap扩展模块的曲折过程
    2、Windows下安装
  • 原文地址:https://blog.csdn.net/sinat_27798375/article/details/140044823