第六部分:底层原理与架构设计

目录

第六部分:底层原理与架构设计

6.1. jQuery 源码解析与架构思想

6.1.1. 整体架构设计:沙箱模式 (IIFE) 与变量隔离

6.1.2. 无 new 构建机制:jQuery.fn.init 的巧妙返回

6.1.3. 链式调用实现:return this 的奥秘

6.2. 原型与扩展

6.2.1. 原型链:jQuery.fn === jQuery.prototype

6.2.2. 深浅拷贝:$.extend() 源码逻辑与插件开发基础

6.2.3. 冲突处理:$.noConflict() 源码解析与多库共存

6.3. 核心引擎剖析

6.3.1. Sizzle 选择器引擎:词法分析 -> 块匹配 -> 过滤流程

6.3.2. 回溯查找:pushStack 方法维护栈结构

6.3.3. 钩子机制:jQuery.cssHooks, jQuery.valHooks 的扩展能力

6.4. 插件开发与生态

6.4.1. 插件开发规范:标准模板、闭包封装与默认配置

6.4.2. 最佳实践:链式调用保持、单例模式与数据管理

6.4.3. 经典插件生态:轮播、弹窗与表单验证的集成思路

6.5. 现代工具方法集

6.5.1. 数组与对象处理:$.map(), $.grep(), $.each(), $.merge()

6.5.2. 类型检测:$.type() 与 $.isPlainObject()

6.5.3. 函数绑定:$.proxy() 与 this 指向修正(对比 ES6 Arrow Functions)


6.1. jQuery 源码解析与架构思想

6.1.1. 整体架构设计:沙箱模式 (IIFE) 与变量隔离

优秀的库设计首先要解决的是“领土问题”。

设计要素

实现方式

核心价值

变量隔离

(function(window) { ... })(window)

内部 var 变量外部不可访问,避免命名冲突。

全局注入

window.jQuery = window.$ = jQuery

仅向全局暴露两个变量,接口极简。

依赖管理

参数传入 window

压缩优化时可将 window 压缩为 w,提升压缩率。

jQuery 的源码整体被包裹在一个立即执行函数表达式(IIFE)中,这种沙箱模式构建了一个私有的作用域,确保内部定义的变量、辅助函数不会污染全局命名空间,同时也防止外部意外修改内部状态。

/* 源码简化结构演示 */

(function(window, undefined) {

    var jQuery = function(selector) {

        return new jQuery.fn.init(selector); /* 内部构造逻辑 */

    };

    /* 内部使用的工具函数,外部无法直接调用 */

    var internalHelper = function() { /* ... */ };

    /* 将 jQuery 挂载到全局对象 */

    window.jQuery = window.$ = jQuery;

})(window); /* 立即执行,传入 window 对象 */

/* 尝试在外部访问内部变量 */

// console.log(internalHelper); /* 报错:internalHelper is not defined */

这就好比在一片公共土地上圈出了一块私人领地。

沙箱模式就像是盖了一座全封闭的工厂,工厂内部的机器(变量)和流程对外不可见,只在门口开了一个小小的收发室(window.$),外界只能通过这个窗口与工厂交互,既保护了商业机密,也维持了街道的整洁。

6.1.2. 无 new 构建机制:jQuery.fn.init 的巧妙返回

在使用 jQuery 时,无需像传统面向对象那样手动 new 一个实例,直接 $('div') 即可得到一个 jQuery 对象。

这种便捷性源于其内部实现了一个工厂模式:jQuery 函数内部通过 new 调用了原型上的 init 方法,自动完成了实例化过程。

/* jQuery 核心构造函数简化源码 */

var jQuery = function(selector, context) {

    /* jQuery 函数直接返回 init 实例,无需用户手动 new */

    return new jQuery.fn.init(selector, context);

};

/* 原型挂载 */

jQuery.fn = jQuery.prototype = {

    constructor: jQuery,

    length: 0,

    /* ... */

};

/* 关键点:将 init 的原型指向 jQuery 的原型 */

jQuery.fn.init.prototype = jQuery.fn;

/* 验证 */

console.log($('body') instanceof jQuery); /* true */

console.log($('body') instanceof jQuery.fn.init); /* true */

这就像是自动售货机与人工小卖部的区别。

对比项

传统构造函数

jQuery 模式

调用方式

var obj = new MyLib();

var obj = $('selector');

写法冗余

需记住 new 关键字

极简,直接调用

实现原理

this 指向新实例

内部 new jQuery.fn.init()

传统模式需要你自己去找店员,说“我要买个东西”,还要遵循一堆流程。

jQuery 模式则是自动售货机,你只需要投币按按钮(调用函数),机器内部自动完成出货流程,对用户来说极其直观,完全感知不到内部复杂的实例化过程。

6.1.3. 链式调用实现:return this 的奥秘

jQuery 最著名的特性之一是其“链式调用”能力,允许在一行代码中连续执行多个操作。

这背后的原理极其简单且优雅:每个原型方法在执行完毕后,都显式返回了 this,即调用该方法的对象本身,从而为下一个方法调用提供上下文。

/* 模拟链式调用实现 */

var myLib = {

    value: 0,

    add: function(num) {

        this.value += num;

        return this; /* 关键:返回对象自身 */

    },

    multiply: function(num) {

        this.value *= num;

        return this; /* 关键:返回对象自身 */

    },

    print: function() {

        console.log(this.value);

        return this; /* 保持链式能力 */

    }

};

/* 实际调用 */

myLib.add(5).multiply(2).print(); /* 输出 10 */

/* jQuery 源码中的实现片段 */

jQuery.fn.extend({

    addClass: function(value) {

        /* ...逻辑代码... */

        return this; /* 绝大多数方法都以此结尾 */

    }

});

这就像是接力赛跑。

第一棒跑完,必须把接力棒交回给跑道,第二棒才能继续跑。

return this 就是把“当前对象”这根接力棒交回给调用者,让下一个方法能够无缝衔接。

如果哪个方法中途“掉棒”(返回了其他数据),整个链条就断了。

6.2. 原型与扩展

6.2.1. 原型链:jQuery.fn === jQuery.prototype

jQuery.fn 是 jQuery.prototype 的简写别名。

理解这一点对于插件开发至关重要。

属性/方法

归属对象

调用方式

典型成员

静态方法

jQuery (构造函数)

$.ajax()$.each()

工具类函数,与 DOM 无关

实例方法

jQuery.fn (原型)

$('div').css()

操作 DOM 集合的方法

所有 jQuery 对象实例共享的原型方法都定义在这个对象上,将工具方法与实例方法分离,极大地节省了内存开销。

/* 验证两者指向同一内存地址 */

console.log(jQuery.fn === jQuery.prototype); /* true */

/* 扩展实例方法 (插件开发的本质) */

jQuery.fn.highlight = function(color) {

    /* this 指向当前的 jQuery 实例对象 */

    this.css('background-color', color);

    return this; /* 保持链式 */

};

/* 立即可用 */

$('p').highlight('yellow');

原型链就像是“族谱”或“家规”。

jQuery 是老祖宗,jQuery.fn 是家规手册。

老祖宗规定,所有出生的子孙(jQuery 实例对象)都必须遵守家规。

只要在家规手册上加一条规则,所有子孙立刻学会这项新技能,而不需要给每个人单独培训。

6.2.2. 深浅拷贝:$.extend() 源码逻辑与插件开发基础

$.extend() 是 jQuery 中实现对象合并的核心工具,同时也是插件开发时合并默认配置与用户配置的基础。

调用形式

第一个参数

行为描述

浅拷贝

false (默认)

遇到对象属性只复制引用,修改会互不影响。

深拷贝

true

递归复制对象内部结构,生成全新的副本。

插件扩展

jQuery.fn

将对象属性复制到 jQuery 原型上,实现插件挂载。

其逻辑核心在于对 deep 参数的判断,决定了是递归复制(深拷贝)还是仅复制引用(浅拷贝)。

var defaults = { mode: 'edit', config: { theme: 'dark' } };

var options = { config: { theme: 'light' } };

/* 浅拷贝 - 引用被覆盖,defaults.config 丢失 */

var settings1 = $.extend({}, defaults, options);

/* 结果: { mode: 'edit', config: { theme: 'light' } } */

/* 深拷贝 - 递归合并,保留 defaults 中未覆盖的属性 */

var settings2 = $.extend(true, {}, defaults, options);

/* 结果: { mode: 'edit', config: { theme: 'light' } } */

/* 注:如果 options 未提供其他 config 属性,defaults 中的属性会被保留 */

/* 插件开发标准模板 */

$.fn.myPlugin = function(options) {

    var opts = $.extend({}, $.fn.myPlugin.defaults, options);

    /* 使用合并后的配置 opts 进行逻辑处理 */

};

$.fn.myPlugin.defaults = { color: 'red', size: 12 }; /* 默认配置暴露出来以便修改 */

深浅拷贝就像是复印文件。

浅拷贝只是在复印件上写了个“详见原件”,原件改了,复印件看到的也变了。

深拷贝则是把原件里的每一张图、每一个字都重新描了一遍,生成了一份完全独立的新文件,原件怎么改都不影响复印件。

6.2.3. 冲突处理:$.noConflict() 源码解析与多库共存

在 jQuery 流行之初,$ 符号是极其抢手的变量名。

为了解决与其他库(如 Prototype、MooTools)的冲突,jQuery 提供了 $.noConflict(),其原理是在库加载时先保存全局中现有的 $,调用该方法时再恢复旧值。

/* 模拟源码逻辑 */

var _$ = window.$; /* 加载时:先备份全局原有的 $ */

jQuery.noConflict = function(removeAll) {

    if (removeAll) {

        window.jQuery = undefined; /* 彻底让出 jQuery 变量 */

    }

    window.$ = _$; /* 恢复备份的旧 $ */

    return jQuery; /* 返回 jQuery 对象引用,用于重命名 */

};

/* 实际使用场景 */

/* 场景一:让出 $,仅使用 jQuery */

$.noConflict();

jQuery('div').hide(); /* 只能用全名 */

/* 场景二:自定义别名 */

var j = $.noConflict();

j('div').hide(); /* 使用 j 作为别名 */

/* 场景三:在闭包内安全使用 $ */

$.noConflict();

(function($) {

    /* 这里的 $ 局部变量指向 jQuery,不受外部影响 */

    $('div').show();

})(jQuery);

这就像是绅士的礼仪。

jQuery 进门发现座位($)上已经有人了,它先把那个人记在心里(备份),然后自己站着。

当你喊“让座”时,它立刻把座位还给原来的人,并递给你一张写着自己名字(jQuery)的名片,告诉你以后可以用这个名字叫他。

6.3. 核心引擎剖析

6.3.1. Sizzle 选择器引擎:词法分析 -> 块匹配 -> 过滤流程

jQuery 的选择器功能强大,核心依赖于其内置的 Sizzle 引擎。

解析阶段

执行逻辑

示例 (div.card > p:first)

词法分析

将选择器字符串拆分为 Token 数组

['div', '.card', '>', 'p', ':first']

块匹配

从右向左查找,利用原生 API 加速

先找 p,再过滤父级是否为 div.card

过滤筛选

根据关系符 (>~) 和伪类过滤

验证父子关系,执行 :first 逻辑

Sizzle 是一个纯 JavaScript 实现的 CSS 选择器引擎,它不依赖浏览器原生的 querySelectorAll(虽然会优先使用),而是实现了一套完整的解析逻辑,保证了跨浏览器的一致性。

/* Sizzle 伪类扩展:自定义选择器 */

$.expr[':'].inline = function(elem) {

    /* 返回 true 表示匹配成功 */

    return $(elem).css('display') === 'inline';

};

/* 使用自定义选择器 */

$('div:inline').css('border', '1px solid red');

Sizzle 就像是一个精密的侦探。

拿到通缉令(选择器字符串)后,它不会盲目地满城搜查,而是先把通缉令拆解成特征(词法分析)。

如果通缉令说“住在富人区、穿红衣服的人”,侦探会先去富人区蹲点,而不是在全市搜查。

这种“从右向左”的查找策略,大幅缩小了搜索范围,提升了效率。

6.3.2. 回溯查找:pushStack 方法维护栈结构

jQuery 对象内部维护了一个栈结构,用于记录 DOM 查找的历史。

当使用 .find()、.children() 等方法缩小范围时,新结果集会被推入栈顶,而 .end() 方法则能弹出栈顶元素,回退到上一级状态。

这一机制是实现复杂链式操作的关键。

/* 链式操作中的状态回溯 */

$('ul.list') /* 1. 选中所有 ul */

    .find('li') /* 2. 在 ul 中找 li (入栈) */

        .addClass('item') /* 给 li 加样式 */

        .end() /* 3. 回退到 ul (出栈) */

    .addClass('container'); /* 给 ul 加样式 */

/* 源码简化模拟 */

pushStack: function(elems) {

    var ret = jQuery.merge(this.constructor(), elems);

    ret.prevObject = this; /* 关键:保存上一级引用 */

    return ret;

},

end: function() {

    return this.prevObject || this.constructor(null);

}

这就像是浏览器的前进/后退按钮。

  • .find() 就像是点击了一个链接进入新页面,把当前页面压入历史记录栈。
  • .end() 就像是点击后退按钮,浏览器会从栈中取出上一个页面显示。

这种栈结构让 DOM 操作拥有了“记忆”,可以在复杂的层级中自由穿梭。

6.3.3. 钩子机制:jQuery.cssHooks, jQuery.valHooks 的扩展能力

为了解决浏览器兼容性问题,jQuery 引入了钩子机制。

在处理 CSS 属性或表单值时,jQuery 不直接操作 DOM,而是先查询钩子对象。

开发者可以通过覆盖钩子,实现对特殊属性(如 opacity 在 IE 下的处理)或特殊表单元素(如 select-multiple)的自定义逻辑。

钩子类型

作用对象

典型应用

$.cssHooks

.css() 方法

修复 opacity 兼容性,添加自定义属性 margin-center

$.valHooks

.val() 方法

处理 option 标签的取值逻辑,处理单选框/复选框状态。

$.attrHooks

.attr() 方法

兼容旧版 IE  href  type 属性的读写。

 示例:通过 CSS Hook 实现自定义属性 

$.cssHooks.marginCenter = {

    get: function(elem) {

        return $(elem).css('margin-left') === 'auto';

    },

    set: function(elem, value) {

        /* 当设置 marginCenter 为 true 时,自动居中 */

        if (value) {

            $(elem).css({

                'margin-left': 'auto',

                'margin-right': 'auto'

            });

        }

    }

};

/* 使用 */

$('.box').css('marginCenter', true); /* 自动设置水平居中 */

钩子机制就像是操作系统的驱动程序接口。

jQuery 是操作系统,DOM 是硬件。

有些标准硬件直接用就行,但对于特殊的硬件(老旧浏览器或特殊属性),就需要安装特定的“驱动”。

钩子就是这些驱动程序,让上层应用(API 调用)不用关心底层细节,只要调用标准接口即可。

6.4. 插件开发与生态

6.4.1. 插件开发规范:标准模板、闭包封装与默认配置

一个高质量的 jQuery 插件应遵循特定的代码规范,确保其健壮性、可维护性与生态兼容性。

标准模板通常包括 IIFE 封装、避免 $ 冲突、提供默认配置、保持链式调用以及对集合迭代的支持。

/* 标准插件开发模板 */

;(function($, window, document, undefined) {

    /* 1. 构造函数定义 */

    var Plugin = function(element, options) {

        this.elem = element;

        this.$elem = $(element);

        this.options = $.extend({}, Plugin.DEFAULTS, options);

        this.init(); /* 初始化逻辑 */

    };

    /* 2. 默认配置 */

    Plugin.DEFAULTS = {

        color: '#333',

        fontSize: '14px'

    };

    /* 3. 原型方法 */

    Plugin.prototype = {

        init: function() {

            this.$elem.css({

                'color': this.options.color,

                'font-size': this.options.fontSize

            });

        },

        destroy: function() {

            this.$elem.removeData('pluginName'); /* 清理数据 */

        }

    };

    /* 4. 挂载到 jQuery */

    $.fn.pluginName = function(options) {

        return this.each(function() { /* 支持 each 迭代 */

            var $this = $(this);

            var instance = $this.data('pluginName');

            if (!instance) {

                instance = new Plugin(this, options);

                $this.data('pluginName', instance); /* 单例存储 */

            }

            if (typeof options === 'string') {

                instance[options](); /* 支持方法调用 */

            }

        });

    };

})(jQuery, window, document);

规范要素

要求

目的

闭包封装

(function($){...})(jQuery)

避免污染全局,解决 $ 冲突。

分号开头

;(function...

防止前一个脚本未闭合导致的错误。

链式保持

return this.each(...)

确保插件方法返回 jQuery 对象本身。

单例模式

data() 存储实例

防止重复初始化,便于获取插件状态。

这就像是工业化的模具设计。

标准模板定义了插件的“安全壳”和“接口槽”。

无论内部逻辑多复杂,外壳必须符合标准尺寸,这样用户在使用时,无论是拧螺丝(配置参数)还是接线(方法调用),都能与现有的系统完美适配。

6.4.2. 最佳实践:链式调用保持、单例模式与数据管理

在插件开发中,不仅要实现功能,更要关注性能与用户体验。

实践模式

错误示例

正确示例

原因分析

返回值

return this.css(...)

return this.each(...)

支持集合操作,保证 this 指向正确。

状态存储

全局变量存储

$(elem).data('key')

避免内存泄漏,关联数据与元素生命周期。

默认配置

硬编码在逻辑中

$.extend(defaults, opts)

提高灵活性,用户可覆盖配置。

核心原则包括:始终保持链式调用能力、使用 data() 方法管理实例状态、以及提供合理的销毁机制。

/* 最佳实践示范 */

$.fn.smartToggle = function(options) {

    var settings = $.extend({ speed: 'fast' }, options);

    return this.each(function() {

        var $btn = $(this);

        var state = $btn.data('toggle-state'); /* 读取状态 */

        if (!state) {

            $btn.data('toggle-state', { count: 0 }); /* 初始化状态 */

        }

        $btn.on('click', function() {

            $btn.toggleClass('active');

            var count = $btn.data('toggle-state').count + 1;

            $btn.data('toggle-state').count = count;

            console.log('点击次数:', count);

        });

    });

};

插件开发就像是装修房子。

链式调用保证了装修完一个房间能立刻装修下一个,不用重新请人。

数据管理则是给每个房间装了独立的电表,互不干扰。

而单例模式确保了同一个房间不会被两个装修队同时乱改,避免了“撞车”事故。

6.4.3. 经典插件生态:轮播、弹窗与表单验证的集成思路

jQuery 生态中诞生了无数经典插件,它们解决特定问题的思路至今仍有借鉴价值。

插件类型

代表作

核心逻辑

集成难点

轮播图

Slick, Owl Carousel

定时器 + CSS Transform 位移

触屏事件支持、响应式宽度计算

弹窗层

Layer, Bootstrap Modal

遮罩层管理 + 焦点捕获 + ESC 关闭

z-index 层级管理、Body 滚动锁定

表单验证

jQuery Validation

规则对象匹配 + 事件监听

异步验证、错误提示的 DOM 插入位置

理解这些插件的集成逻辑,有助于在现代开发中更好地复用代码或迁移方案。

/* 集成思路示例:简易表单验证 */

$.fn.simpleValidate = function(rules) {

    return this.on('submit', function(e) {

        var $form = $(this);

        var isValid = true;

        /* 遍历规则 */

        $.each(rules, function(field, rule) {

            var val = $form.find('[name=' + field + ']').val();

            if (!rule.test(val)) {

                alert(rule.message || '格式错误');

                isValid = false;

                return false; /* 跳出循环 */

            }

        });

        if (!isValid) e.preventDefault(); /* 阻止提交 */

    });

};

/* 调用 */

$('#myForm').simpleValidate({

    username: { test: /\w{3,}/, message: '用户名至少3位' },

    email: { test: /@/, message: '邮箱格式不正确' }

});

经典插件就像是建筑里的预制件。

轮播图是旋转门,弹窗层是活动隔断,表单验证是安检门。

它们把复杂的机械结构封装在内部,只留下简单的安装接口。

理解了这些预制件的安装原理,即使面对现代框架,也能快速构建出功能完善的建筑。

6.5. 现代工具方法集

6.5.1. 数组与对象处理:$.map(), $.grep(), $.each(), $.merge()

在 ES5 标准普及之前,jQuery 提供了一套完善的数组与对象处理工具,弥补了原生 JavaScript 在集合操作上的不足。

方法

等效原生方法

核心差异

$.each()

Array.prototype.forEach

可遍历对象,回调参数顺序为。

$.map()

Array.prototype.map

可遍历对象,返回 null 时自动过滤该项。

$.grep()

Array.prototype.filter

仅用于数组,无法过滤对象。

$.merge()

Array.prototype.concat

原地修改第一个数组,而非生成新数组。

虽然现代开发推荐优先使用原生方法,但在维护旧项目或处理类数组对象时,这些工具依然高效。

var arr = [1, 2, 3, 4, 5];

/* $.grep: 过滤偶数 */

var evens = $.grep(arr, function(n, i) {

    return n % 2 === 0;

}); /* 结果: [2, 4] */

/* $.map: 值翻倍 */

var doubled = $.map(arr, function(n) {

    return n * 2;

}); /* 结果: [2, 4, 6, 8, 10] */

/* $.merge: 合并数组 (注意是修改原数组) */

var target = [1, 2];

$.merge(target, [3, 4]);

/* target 变为 [1, 2, 3, 4],这点与 concat 不同 */

这些工具方法就像是瑞士军刀里的基础工具。

虽然现在有了专业的螺丝刀套装(原生 ES6 数组方法),但在某些紧急情况或老旧环境中,这把军刀里的小剪刀($.grep)和小锉刀($.merge)依然能快速解决问题,且对“不规则的木头”(类数组对象)有更好的适应性。

6.5.2. 类型检测:$.type() 与 $.isPlainObject()

JavaScript 的 typeof 运算符在判断 null、Array、Date 等类型时存在诸多缺陷。

检测对象

typeof 结果

$.type() 结果

$.isPlainObject()

null

"object"

"null"

false

[1, 2]

"object"

"array"

false

new Date()

"object"

"date"

false

{ a: 1 }

"object"

"object"

true

function(){}

"function"

"function"

false

jQuery 封装了 $.type() 方法,利用 Object.prototype.toString.call() 返回精确的类型字符串,并提供了 $.isPlainObject() 用于判断纯粹的对象字面量。

/* 精确类型判断 */

console.log($.type(null)); /* "null" */

console.log($.type(document.body)); /* "element" (原生 typeof 是 object) */

console.log($.type(/regex/)); /* "regexp" */

/* 纯对象判断 (常用于深拷贝或插件配置) */

var obj = { name: 'test' };

var cls = new Object(); /* 虽然 cls 也是对象,但某些场景被视为纯净 */

var inst = new $.fn.init(); /* 实例对象 */

console.log($.isPlainObject(obj)); /* true */

console.log($.isPlainObject(inst)); /* false */

typeof 就像是一个只能分辨“有腿”和“没腿”的幼稚门卫,把桌子、椅子和人都统称为“有腿的物体”。

$.type() 则是配备了 DNA 检测仪的高级安检员,能透过表象准确识别出“这是桌子”、“那是椅子”。

而 $.isPlainObject() 更是专门的“纯净度检测”,只对那些没有任何额外构造函数污染的“纯净”数据放行。

6.5.3. 函数绑定:$.proxy() 与 this 指向修正(对比 ES6 Arrow Functions)

在事件绑定或定时器回调中,函数内部的 this 往往会丢失或改变。

方案

语法

原理

兼容性

$.proxy()

$.proxy(fn, context)

利用闭包保存 context

全兼容

ES5 Bind

fn.bind(context)

原生方法,返回新函数

IE9+

ES6 Arrow

() => { ... }

词法作用域继承 this

现代浏览器

jQuery 提供了 $.proxy() 方法,用于强制绑定函数的执行上下文。

虽然在 ES6 中箭头函数已完美解决此问题,但在旧代码重构中 $.proxy() 仍有一席之地。

var obj = {

    name: 'Module A',

    init: function() {

        /* 错误示范:setTimeout 回调中 this 指向 window */

        setTimeout(function() {

            console.log(this.name); /* 输出: undefined */

        }, 100);

        /* 方案一:使用 $.proxy 修正 this */

        setTimeout($.proxy(function() {

            console.log(this.name); /* 输出: Module A */

        }, this), 100);

        /* 方案二:ES6 箭头函数 (推荐) */

        setTimeout(() => {

            console.log(this.name); /* 输出: Module A */

        }, 100);

    }

};

/* 事件处理中的 proxy */

$('#btn').on('click', $.proxy(obj.handler, obj));

/* 确保 handler 中的 this 指向 obj 而非 #btn */

this 的指向问题就像是变形金刚的身份识别。

普通函数是变色龙,到了哪个环境就变成那个环境的颜色。

$.proxy() 就像是给变形金刚穿上了一件写有名字的固定制服,无论走到哪里,大家都知道它是谁。

而箭头函数则像是拥有坚定信念的战士,无论身处何地,始终记得自己的家乡是哪里。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐