jQuery高级用法实战:事件委托、Deferred与插件化开发
2026/9/18 4:39:07 网站建设 项目流程

有很多人说“jQuery 早就过时了,新项目谁还用”,但只要你还在做前端,就会频繁遇到这类场景:老后台管理系统、服务端渲染页面、营销活动落地页,或者一个连打包工具都没有的纯静态页面。这些地方恰恰是 jQuery 高级用法真正发挥作用的地方。有人觉得 jQuery 就是个廉价的 DOM 操作库,能实现$('#id').click()就万事大吉,但真正让我对这套库改观,是在接手一个运行多年、到处是历史遗留代码的项目之后——我把它的链式调用、事件委托、异步编排和插件封装方式梳理清楚,整个项目的维护成本肉眼可见地降了下来。

这篇文章不写那种“API 字典式”的罗列,而是把我在实际项目中验证过的高级玩法拆开讲:选择器与链式操作怎么写出可维护性、事件机制怎么避免动态内容反复绑定、Deferred 怎么把 AJAX 回调编排成清晰的逻辑流、怎么手写一个 Bootstrap 风格下拉菜单插件,以及原生 JS + jQuery + AJAX + ECharts 混合开发时踩过的坑。文章也覆盖面试里高频的“jQuery 高级用法”考点。适合两类人看:一类是刚从前端框架回到 jQuery 项目、需要快速建立完整认知的开发者,另一类是写 jQuery 写了几年、但一直靠“复制粘贴”应付业务、想把底层逻辑吃透的朋友。

1. 先聊清楚:jQuery 的高级用法到底高级在哪

聊高级用法之前,得先把一件事掰扯明白:jQuery 的“高级”从来不是某个冷门 API,而是一套工程上的驾驭能力。很多人觉得高级就是会用.trigger()、会写$.Deferred,其实不全是。真正的高级,是你在一个没有模块系统、没有编译器、可能连 ES6 都用不完整的页面上,仍然能把代码组织成职责清晰、能维护、能扩展的结构。

1.1 它解决了什么问题:批量 DOM 操作和跨浏览器一致性

jQuery 的核心价值从来不是“快”,而是“一致”。在早期浏览器各自为战的年代,addEventListenerattachEvent不统一,getElementsByClassName有的浏览器不支持,ajax对象的创建方式也千差万别。jQuery 把这层差异全封装掉了,你写一份代码,在 IE8 和 Chrome 里表现相同。虽然现在原生 API 已经很齐整,但遗留项目和某些企业内网环境里,旧内核浏览器依然存在,jQuery 的兼容价值没有完全归零。

第二个价值是批量操作。原生 JS 拿到一组节点后,要自己循环处理;jQuery 里$('li').addClass('on')一行代码就是遍历全部节点。这种“隐式迭代”的设计,配合链式调用,能把命令式代码压缩成声明式的操作流,阅读起来像在描述结果,而不是描述过程。

1.2 别神化也别踩死:什么场景该放手

我也见过不少团队一边骂 jQuery 一边把项目搞到无法维护,根本不是库的问题,是把十几年前的写法原封不动带到了今天。全局函数堆在一起、选择器到处重复、业务逻辑全部塞在$(document).ready里——这时候换 Vue 也是灾难。如果你决定在项目里用 jQuery,就等同于接受了“用工程手段约束自己”的责任。

反过来,如果一个新项目完全用现代框架、组件化开发、构建工具齐全,那确实没有必要硬塞 jQuery。jQuery 的高频率操作是 DOM 遍历和事件绑定,这种命令式写法在数据驱动视图的框架里反而显得绕。我的经验是:原生 JS 和框架能轻松搞定的场景,不引入;但引入之后,就要把它往“有结构”的方向去用。

2. 选择器和链式操作:从“找准元素”到“高效操作元素集合”

打开控制台随手敲$('div.class')很容易,但到了真实项目里,选择器写得好不好直接关系到两个东西:一是运行时性能,二是代码的语义清晰度。

2.1 选择器优化:缓存优先,从右往左思考

利用选择器开销最常见的误区是每用一次就全文档查询一次。当你写$('.item').css('color', 'red')又写$('.item').css('background', 'blue'),等价于扫了两遍 DOM。正确做法是先缓存:

const $items = $('.item'); $items.css('color', 'red'); $items.css('background', 'blue');

当然,更好的是合并链式操作:

$('.item').css({ color: 'red', background: 'blue' });

另一个不常被注意到的点是选择器书写顺序。Sizzle 引擎(jQuery 底层的选择器实现)解析复杂选择器时是从右往左匹配的,也就是说$('.list .item')会先找到所有.item,再过滤祖先是不是.list。这个顺序在页面节点数量庞大时会有感知差异。所以写选择器时,尽量让最右侧的限制条件更精确、数量更少。

2.2 链式调用的核心:隐式遍历与 end()

jQuery 的链式调用之所以能一直向后操作,核心在于每个操作方法返回的都是一个新的 jQuery 对象,而且操作本身自带循环。举个例子:

$('.box') .addClass('active') .find('span') .text('已激活') .end() .attr('data-status', 'on');

这段代码先给.box加类,找到内部的span,把文本改成“已激活”,然后.end()把上下文退回上一次操作的对象(也就是.box),最后重新给.box设置属性。end()维护了一个内部栈,出栈之后就能回到之前的选择结果——这是链式编程里最容易被忽略但最优雅的机制。

有些复杂场景还用到addBack(),它会把上一次的集合和当前集合合并:

$('.box').find('span').addBack().addClass('highlight');

这里span.box自身都会加上highlight。对于多层级结构处理,这种写法比反复声明中间变量干净得多。

2.3 jQuery 对象与原生 DOM 的边界转换

$(domNode)可以把原生节点包成 jQuery 对象,get(0)[0]可以把 jQuery 对象转回原生 DOM。这个转换看起来基础,但实战里很容易踩坑。比如你拿到一个className,用 jQuery 对象访问className得到的是undefined

const $el = $('#username'); $el.className; // undefined $el[0].className; // 'username'

另一个常见场景是事件对象:在each遍历里用this,这个this是原生节点,不是 jQuery 对象。想要用链式方法就得包一层:

$('.item').each(function () { const $this = $(this); $this.css('padding-left', '12px'); });

很多人会在这儿挣扎,其实本质就一句话:jQuery 方法只能作用于 jQuery 对象,原生属性和方法只能作用于原生节点。需要哪个用哪个,别混淆。

3. 事件机制升级:委托、命名空间和自定义事件

jQuery 事件相关的 API 在多数项目里只用到了冰山一角,最常见的写法是直接.click(function(){}),但复杂交互页面一旦动态渲染节点,直接绑定就会产生“绑不到”或“越绑越多”的问题。

3.1 用.on()统一管理事件,别再背 bind/live/delegate 了

老代码里经常看到.bind().live().delegate(),这几个方法在 jQuery 3.0 后已经移除。现代 jQuery 里,所有事件绑定都统一到.on(),所有解绑都用.off()。核心逻辑是:.on()的第一个参数是事件类型,第二个参数可选,传了就是事件委托的选择器。

$('#list').on('click', '.item', function (e) { console.log('Item clicked', e.currentTarget); });

一旦你习惯了这种写法,就会发现历史上livedelegate的区别基本不用再背了——它们当年的功能被.on()的委托参数统一收纳。

3.2 动态内容的事件委托:绑定一次处处生效

最常见的动态列表是后端返回数据后前端把<li>渲染进去。如果你的绑定发生在渲染之前,用普通.click()就绑不上;如果渲染之后重新绑定,再次刷新数据时事件会重复叠加。事件委托把监听挂在父容器上,通过事件冒泡捕获子节点,这样就绕开了“动态添加”的困境。

关键点是委托选择器在事件触发时才做匹配,而不是绑定时就确定对象。这个机制非常像“统一前台挂号,不关心里面医生是谁,只管到窗口办事”。

我在项目里习惯给每个列表容器只挂一次委托,哪怕列表内容被清空后重新渲染,事件依然有效:

const $tbody = $('#dataTable tbody'); $tbody.off('click', 'tr.edit-btn').on('click', 'tr.edit-btn', function () { const $row = $(this).closest('tr'); const id = $row.data('id'); editRecord(id); });

这里先.off().on(),是一个防御性习惯,防止模块被反复初始化时叠加绑定。

3.3 命名空间:精准解绑,不误伤其他事件

如果你在同一个元素上绑了多个click,想只解绑其中一个,.off()不传参数会全部解除。这时候事件命名空间的价值就出来了:

$('#btn') .on('click.save', saveHandler) .on('click.log', () => console.log('click logged')); $('#btn').off('click.save');

.save就是命名空间,解绑时只移除click里带.save的那一个。这个技巧在大型模块里特别好用,尤其是组件销毁时只清理自己的事件,不动别的模块的监听。

自定义事件也用得上命名空间,比如:

$(document).on('order:update', function (e, orderId) { refreshOrderPanel(orderId); loadLog(orderId); });

有命名空间的话,后续只要.off('order:update')就能整个移除。

3.4 自定义事件:让业务模块从“互相调用”变成“各自监听”

自定义事件值得单独说说。两个模块之间如果直接调用对方的方法,代码会耦合得越来越紧。比如订单模块更新后,需要让日志模块追加一条记录、让看板模块刷新数字、让通知模块弹一个提醒,如果订单模块里直接调用三个模块的方法,过两个版本就变成意大利面条。

稳妥的做法是订单模块只负责发布一个事件,其他模块各自监听、各自处理:

// 操作完成时 $(document).trigger('order:updated', { id: orderId, status: 'paid' }); // 其他模块中 $(document).on('order:updated', function (e, data) { updateLogList(data.id); refreshStats(); notify(data.status); });

这样的解耦在多个模块并行开发时非常舒服。模块只需要知道“对外发布什么事件”,不需要知道“谁在听”。这也是把 jQuery 从“工具库”往“轻量事件总线”方向使用的方式。

4. 别再回调地狱了:用 Deferred 把 AJAX 编排成清晰异步逻辑

前端异步发展经历了回调 → Promise → async/await 三个阶段。jQuery 从 1.5 起引入了 Deferred 对象,本质上就是自己实现了一版 Promise。在老项目里,你没法让所有历史代码都升级到 async/await,但至少可以把嵌套回调改写成链式逻辑。

4.1 容易被忽略的 $.ajax 配置项

大部分人用$.ajax只传urlmethoddata,跑通了就不管了。但在生产环境里,有几个配置项非常关键:

  • timeout:请求超时时间。不设的话,一个慢接口能把整个交互卡到死。
  • cache: false:对 GET 请求追加随机参数,防止浏览器“读缓存”。
  • beforeSend:在请求发出前设置自定义 Header,常用于携带鉴权 token。
  • xhrFields.withCredentials:跨域请求需要携带 Cookie 时开启。
  • dataType:明确告诉 jQuery 期望的返回类型,例如'json',不只是声明,还会影响响应预处理。
$.ajax({ url: '/api/order/list', method: 'GET', dataType: 'json', timeout: 8000, cache: false, beforeSend: function (xhr) { xhr.setRequestHeader('Authorization', 'Bearer ' + getToken()); }, data: { page: 1, size: 20 } }).then(res => { renderOrderList(res.rows); }).fail(err => { showToast('加载订单失败'); });

.then()里的回调返回值如果是普通值,会直接传给下一个.then();如果返回的是新的 Deferred/Promise,后续回调会等待它完成。理解这个特性,就掌握了链式异步的核心。

4.2 $.when 并行,.then 串行

多个请求同时发出、全部返回后再统一处理,用$.when

const reqUsers = $.get('/api/users'); const reqOrders = $.get('/api/orders'); const reqGoods = $.get('/api/goods'); $.when(reqUsers, reqOrders, reqGoods).then(function (usersRes, ordersRes, goodsRes) { render(usersRes[0], ordersRes[0], goodsRes[0]); });

$.when的参数传入多个 Deferred 对象,then的回调里每个结果都是一个数组,第一个元素是响应数据。这一手在实际项目中避免了“先请求 A,成功后再请求 B,等 B 回来再并发请求 C”的层层嵌套。

如果多个请求存在依赖关系,比如要先拿到文章详情再加载评论,更合理的写法是串行链:

$.get('/api/posts/1') .then(post => $.get(`/api/comments/${post.id}`)) .then(comments => renderComments(comments)) .fail(() => showToast('加载评论失败'));

这种结构读起来像同步代码,实际是逐级依赖的异步流。每次.then()返回新的请求,整个链会等待前一个请求结束。

4.3 jQuery Deferred 与原生 Promise 混用时的边界

jQuery 3.0 之后,$.Deferred和 Promise/A+ 规范完全兼容,但老版本(1.x/2.x)有细微差别。在老版本中,$.ajax().then()的第二个参数是fail的别名,而原生 Promise 的.then()第二个参数接收的是“rejection 回调”。混用时要小心,一个比较稳的兼容处理是统一使用.then().fail()风格,而不是把错误处理全挤在.then()的第二个参数里。

还有个小坑:老版本中 jQuery Deferred 的.catch()方法不可用,建议使用.fail().finally()(从 jQuery 3.0 开始支持)。另外,永远不要在$.when里传单个数组作为参数,要用apply展开:

const requests = urls.map(url => $.get(url)); $.when.apply($, requests).then(function () { const results = Array.prototype.slice.call(arguments).map(r => r[0]); renderAll(results); });

这种动态数量请求的处理,页面初始化需要并发加载多个接口时特别常用。

5. 插件化实战:手写一个 Bootstrap 风格下拉菜单

“用 jQuery 模拟 Bootstrap 的静态下拉菜单”是一个在搜索里出现频率很高的需求。原因很简单:很多后台模板只引入了 Bootstrap 的 CSS,没引入对应的 JS 文件,或者希望下拉菜单的交互逻辑更轻量、更好定制。我自己在做这类功能时,顺手把它封装成了插件,这一节完整复盘整个过程。

5.1 先拆交互需求,再写代码

很多人一上来就写toggleClass,把代码堆在全局,结果菜单打开了关不掉,点击外部也没反应。动手之前先把交互列清楚:

  • 点击触发按钮,菜单展开或收起。
  • 点击同一组菜单里的其他触发按钮,已展开的应该收起。
  • 点击外部任意区域,所有菜单收起。
  • 按 Esc 键,所有菜单收起。
  • 若菜单靠右对齐,下拉层不要超出视口右侧。

需求清晰后,写代码就是按图索骥,不会漏逻辑。

5.2 HTML/CSS 骨架设计

HTML 结构沿用 Bootstrap 的经典容器结构,点开由position: relative的容器包裹,菜单绝对定位。

<div class="ui-dropdown"> <button class="ui-dropdown-toggle">.ui-dropdown { position: relative; display: inline-block; } .ui-dropdown-menu { display: none; position: absolute; top: 100%; left: 0; min-width: 140px; margin: 4px 0 0; padding: 6px 0; background: #fff; border: 1px solid #ddd; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); z-index: 1000; } .ui-dropdown.open .ui-dropdown-menu { display: block; } .ui-dropdown-menu li { list-style: none; } .ui-dropdown-menu a { display: block; padding: 8px 16px; color: #333; text-decoration: none; } .ui-dropdown-menu a:hover { background: #f5f5f5; } .ui-dropdown-menu .divider { height: 1px; margin: 6px 0; background: #e5e5e5; }

5.3 事件绑定:菜单开合、点击外部关闭、键盘 ESC

交互层面,我选择用事件委托,把监听挂在.ui-dropdown容器上,并在 document 上挂一个处理点击外部和 Esc 的监听。

$(document) .on('click', '.ui-dropdown-toggle', function (e) { e.stopPropagation(); const $this = $(this); const $box = $this.closest('.ui-dropdown'); const isOpen = $box.hasClass('open'); $('.ui-dropdown.open').removeClass('open'); if (!isOpen) { $box.addClass('open'); } }) .on('click', function () { $('.ui-dropdown.open').removeClass('open'); }) .on('keydown', function (e) { if (e.key === 'Escape') { $('.ui-dropdown.open').removeClass('open'); } });

关键点是触发按钮上的e.stopPropagation()。它阻止事件冒泡到 document,否则点击按钮时会立即被 document 的监听关闭菜单,导致“点了没反应”。这是我过去踩过最典型的坑,希望你在自己实现时直接避开。

菜单内部点击某项后的行为视业务而定,一般菜单项会关闭菜单并执行对应动作,这里为通用处理:

$('.ui-dropdown-menu').on('click', 'a', function () { $(this).closest('.ui-dropdown').removeClass('open'); });

5.4 封装成 jQuery 插件的标准模式

功能调通后,最忌讳的就是依赖一堆“全局代码”。把它封装成插件,既能复用,也能统一配置。标准模式如下:

(function ($) { $.fn.uiDropdown = function (options) { const settings = $.extend({ closeOthers: true, closeOnClickOutside: true, escToClose: true }, options); return this.each(function () { const $box = $(this); if ($box.data('ui-dropdown-created')) return; // 防止重复初始化 $box.data('ui-dropdown-created', true); $box.on('click', '.ui-dropdown-toggle', function (e) { e.stopPropagation(); if (settings.closeOthers) { $('.ui-dropdown.open').not($box).removeClass('open'); } $box.toggleClass('open'); }); }); }; $(function () { $('.ui-dropdown').uiDropdown(); }); })(jQuery);

为什么return this.each(...)而不是$.fn.uiDropdown = function() { this.addClass('open'); ... }?因为插件的通用约定是支持选择器选中多个元素,each保证每个元素独立处理。另一个重点是$.extend合并默认参数和用户自定义参数,这样调用方可以只传需要覆盖的项:

$('.ui-dropdown').uiDropdown({ closeOthers: false });

我建议在项目里把这类常用交互收敛成插件或独立模块,哪怕只在一两个页面用到。原因很简单:一周后你再打开这个文件,看到$('.ui-dropdown').uiDropdown()比看十行全局事件绑定要省力得多。

6. 综合协作:原生 JS + jQuery + AJAX + ECharts 做大屏看板

很多项目不是“纯 jQuery 项目”,而是一个混搭工程:部分逻辑用原生 JS,AJAX 请求用 jQuery 封装,图表依赖 ECharts。这种组合在数据大屏、后台统计页面里很常见。我在这里讲一个实际的协作案例和踩坑总结。

6.1 分层结构:数据层、DOM 层、图表层各自负责什么

混搭项目最怕没有底线,什么都往全局放。我的项目里一般分成三层:

  • 数据层:负责用$.ajax拉取接口,处理返回值,转成可视化数据格式。
  • DOM 层:负责页面里列表、统计文案、表单状态的渲染与更新。
  • 图表层:负责 ECharts 图表的初始化、配置更新、销毁。

分层之后,数据请求只出现在数据层函数里,DOM 操作集中到渲染函数里,图表逻辑独立封装。谁出问题了就去对应层排查,不用在一个千行文件里翻来翻去。

6.2 用 $.ajax 拉数据,用状态对象管理渲染

页面加载时会请求多个统计接口,我习惯把页面状态维护在一个对象里:

const state = { loading: false, summary: {}, trend: [], list: [] }; function loadDashboard() { if (state.loading) return; state.loading = true; showLoading(); $.when( $.get('/api/dashboard/summary'), $.get('/api/dashboard/trend'), $.get('/api/dashboard/list') ).then(function (summary, trend, list) { state.summary = summary[0]; state.trend = trend[0]; state.list = list[0]; renderSummary(state.summary); renderTrendChart(state.trend); renderList(state.list); hideLoading(); state.loading = false; }).fail(function () { showToast('看板数据加载失败'); hideLoading(); state.loading = false; }); }

状态对象的出现让“数据从哪里来、到哪儿去”一目了然。后续不管新增多少图表,都遵循这个模式:请求 → 更新 state → 触发渲染。“数据驱动渲染”不是框架的专利,jQuery 项目同样可以这么组织。

6.3 ECharts 的生命周期:init、setOption、dispose、resize

ECharts 和 jQuery 协作最常见的坑是重复初始化。如果每次刷新数据都调用echarts.init,同一个容器会被反复初始化,图表会闪烁甚至叠加。标准做法是只初始化一次,后续更新用setOption

let trendChart = null; function getTrendChart() { const el = document.getElementById('trendChart'); if (!el) return; if (!trendChart) { trendChart = echarts.init(el); } return trendChart; } function renderTrendChart(trendData) { const chart = getTrendChart(); if (!chart) return; chart.setOption({ tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: trendData.map(item => item.date) }, yAxis: { type: 'value' }, series: [{ data: trendData.map(item => item.value), type: 'line', smooth: true }] }); }

另一个容易忽视的地方是窗口尺寸变化时图表不跟着伸缩。ECharts 自带resize(),但要监听窗口事件并手动调用。如果页面里有多个图表,需要统一在 resize 时批量调用:

const charts = []; function registerChart(chartInstance) { charts.push(chartInstance); } $(window).on('resize', function () { charts.forEach(chart => chart.resize()); });

页面销毁或容器被移除时,记得调用chart.dispose()释放内存,否则单页应用反复打开关闭页面,浏览器内存会明显上涨。

6.4 我踩过的三个坑:重复初始化、事件重复绑定、数据闪烁

第一个坑是重复初始化,上面已经讲过,核心是用一个变量缓存实例,不重复init

第二个坑是事件重复绑定。下拉刷新或者筛选条件变化时,如果重新执行了初始化函数,而函数内部又绑定了click,就会一层叠一层。我的办法是:模块初始化函数统一先offon,或者把绑定逻辑拆出去,只执行一次。比如:

function bindEvents() { // 只负责绑定,初始化时调用一次 $('#refreshBtn').on('click', loadDashboard); $('#rangeSelect').on('change', loadDashboard); }

第三个坑是数据闪烁。加载新数据时,如果先清空图表再重新 setOption,视觉效果会很突兀。更顺滑的做法是保留旧数据,等请求成功后再一次性setOption;请求期间用chart.showLoading()提示用户。这个体验层面的细节,很多时候比功能本身更影响口碑。

7. 面试里“高级用法”的高频考点,以及我的答题框架

项目做得多了,被问“jQuery 高级用法”几乎是避免不了的。面试官很少真指望你背 API,更多是想看你对底层机制的理解深度。我遇过高频考点,整理成几组,每一组都给一个可以直接参考的答题思路。

7.1 $.extend 的深浅拷贝,为什么总被问

$.extend是 jQuery 里用途很广的工具方法,常见的场景是合并配置对象。它最容易被追问的知识点是“第一个参数传true代表深合并”。

const target = { a: 1, group: { x: 10, y: 20 } }; const source = { a: 2, group: { y: 30 } }; $.extend(target, source); // target.group 被整个替换成 { y: 30 },x 丢失 $.extend(true, target, source); // target.group 是 { x: 10, y: 30 } 深逐层合并

面试时回答这个问题,先讲浅合并会直接覆盖整个属性,再讲深合并会逐层递归遍历对象属性,接着结合插件开发的默认参数合并场景展开,体现的不只是 API 了解程度,更是对“配置继承”这一设计的理解。顺带可以提一句:$.extend同样可以给对象扩展方法,很多老式工具库都用它来做混入,这和现代框架中“mixin”的思路异曲同工。

7.2 .on() 和 .bind()/.delegate() 的核心区别

.bind()是直接绑定到元素上,.delegate()是托付给父级监听,.on()统一了两者的用法。面试官问这个问题的潜台词一般是:动态添加的节点事件怎么处理?因为.bind()对动态添加的节点无能为力,委托方式通过冒泡则是有效的。

更进一步的回答是强调“委托也有限制”:事件必须能冒泡到委托目标才能被捕获,像focusblur这类不冒泡的事件可以用focusinfocusout替代。另外委托层级越深,匹配判断成本越高,所以不要什么都委托到document上,尽量委托到最近的静态父容器。

7.3 判断元素是否进入视口:从 getBoundingClientRect 说起

这个考点和 jQuery 标签绑定在一起,是因为现在很多做“滚动加载动画”的需求都要判断元素露出屏幕的比例。基于 jQuery 的经典实现:

function isInViewport($el, threshold = 0) { const rect = $el[0].getBoundingClientRect(); return ( rect.top <= window.innerHeight - threshold && rect.bottom >= threshold ); } let ticking = false; $(window).on('scroll', function () { if (!ticking) { requestAnimationFrame(() => { $('.animate-on-scroll').each(function () { if (isInViewport($(this), 50)) { $(this).addClass('visible'); } }); ticking = false; }); ticking = true; } });

我在回答时会把重点放在“为什么要用requestAnimationFrame节流”和“getBoundingClientRect比读取offsetTop更准确”这两点上。

7.4 .data()、.prop()、.serializeArray():平时少见但实战很香

这三个方法在面试里不一定被直接点名,但经常以场景题出现。

.data()用于从元素上读取>const formData = $('#filterForm').serializeArray(); console.log(formData); // [{name: 'keyword', value: '手机'}, {name: 'status', value: '1'}] const searchParams = $.param(formData); // keyword=%E6%89%8B%E6%9C%BA&status=1

拿到这种数组后,你还可以用$.map或原生map转换成对象,再传给后端。这个组合在处理筛选条件、批量操作表单时非常实用。


最后分享一个小技巧:我在维护 jQuery 项目时,总会先用十分钟把项目里所有反复出现的 DOM 操作和事件绑定列出来,然后想一下哪些是“一次性逻辑”,哪些是“可以抽象成插件/组件的逻辑”。这个习惯帮我把代码量降下来了不少。对于还在用 jQuery 的老项目,别急着推翻重写,先把这些高级用法体系化地落地,你会发现它没你想的那么不堪。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询