7.2 环境检测与 DOM 工具 器械台的第二格放着仪表与接口:一类是"判断手上的东西是什么"的检测工具,一类是"判断浏览器能做什么"的能力探测,还有一类处理 DOM 树关系的独立函数。本节还承接一章旧债——原教程的插件章在本册被合并至此,因为插件机制本质上只是给 jQuery 原型挂方法的固定仪式,属于这台机器的官方加装接口。 本节摘要:类型判断族( 、 、 、 、 )用于防御性编码,如今大半可由原生替代;能力探测的正解是特性检测而非浏览器嗅探;独立 DOM 工具 判定包含关系;插件机制 把自定义方法挂上 jQuery 原型,是类库生态的地基。 类型判断族:先问是什么,再决定怎么动 这族函数的典型岗位是接口入口的防御:函数答应接收配置对象,先 验一下身;答应接收数字,先 拦一拦。
器械台的第二格放着仪表与接口:一类是"判断手上的东西是什么"的检测工具,一类是"判断浏览器能做什么"的能力探测,还有一类处理 DOM 树关系的独立函数。本节还承接一章旧债——原教程的插件章在本册被合并至此,因为插件机制本质上只是给 jQuery 原型挂方法的固定仪式,属于这台机器的官方加装接口。
本节摘要:类型判断族(
isPlainObject、isFunction、isNumeric、isEmptyObject、type)用于防御性编码,如今大半可由原生替代;能力探测的正解是特性检测而非浏览器嗅探;独立 DOM 工具$.contains判定包含关系;插件机制$.fn.extend把自定义方法挂上 jQuery 原型,是类库生态的地基。
$.isPlainObject({ a: 1 }); // true:纯对象字面量或 new Object $.isPlainObject(document.body); // false:DOM 节点不算 $.isFunction(function () {}); // true $.isNumeric('42'); // true:数字字符串也算 $.isNumeric('4ab'); // false $.isEmptyObject({}); // true:没有任何可枚举属性 $.type(/re/); // 'regexp':返回小写类型名字符串
这族函数的典型岗位是接口入口的防御:函数答应接收配置对象,先 isPlainObject 验一下身;答应接收数字,先 isNumeric 拦一拦。原生对照里,typeof、Array.isArray、instanceof 覆盖了多数场景,isNumeric 的"字符串数字也算"与 type 的统一字符串命名是它的方言特性——type 随 jQuery 3.3 起已标记废弃,新代码请用 typeof 与 Object.prototype.toString 组合。
判断浏览器能力的正路是特性检测——直接问某个 API 存不存在,而不是猜浏览器版本。历史上 $.browser(按 user-agent 嗅探)早已废弃,$.support 是官方给的能力检测面板,但现代开发里更推荐按需手查:
// 特征检测的直觉写法:用之前先确认存在 if ('IntersectionObserver' in window) { // 用新接口做懒加载 } else { // 退回滚动事件的旧方案 }
这条思路在本册出现过好几次——第 4 章判断 closest 存不存在、第 6 章判断 fetch 与 JSONP 走哪条路,本质都是特性检测。把它上升为习惯:任何"新 API 上了老浏览器会怎样"的担忧,答案都是运行前查一次,而不是查一次浏览器名单然后祈祷。
$.contains(容器, 可能的后代) 判定节点包含关系,委托代码里偶有出场(判断点击是否落在某区域内,比 closest 更轻)。原生对应是 Node.contains,同名同义,直接平移。
插件机制是本节的重头。所谓插件,就是把自定义方法挂上 $.fn(jQuery 原型),让所有 jQuery 对象立刻拥有它:
(function ($) { $.fn.tag = function (options) { // 规范一:支持配置合并(上一节的 extend 正好上岗) var opts = $.extend({ cls: 'tagged' }, options); // 规范二:this 是当前集合,each 处理每个元素 return this.each(function () { $(this).addClass(opts.cls); }); // 规范三:return this 保持链式 }; })(jQuery); $('.item').tag({ cls: 'picked' }).fadeOut('fast'); // 挂上即用,链式不断
三条规范各有来由。外层立即执行函数并把 jQuery 作为 $ 传入,是防 $ 被其他库占用(呼应 7.5 的 noConflict);配置合并让插件可定制;return this 是插件能继续链式的全部秘密——上一节"方法返回集合"的公理在这里变成一条纪律。原教程另立一章讲的 jQuery UI,本质是官方维护的大型插件集与交互组件库,现代项目里它的多数组件已被轻量方案替代,但"组件即插件"的思想在各类类库生态里一脉相承。
检测工具的价值要放在真实场景里看。背景是标本页上的一个公开小函数:它接收配置对象并渲染候诊条目,最初版本天真地信任一切输入,页面一接错参数就静默罢工。
操作分两步。第一步在入口装安检:
function renderQueue(config) { if (!$.isPlainObject(config)) { console.warn('renderQueue 需要 config 对象,收到的是', $.type(config)); return; } if (config.max != null && !$.isNumeric(config.max)) { throw new Error('max 必须是数字'); } var items = $.isArray(config.items) ? config.items : []; // 安检通过,进入正常渲染 }
结果:把字符串、null、缺字段的对象分别喂进去,前两者被礼貌拦下并留下线索,第三者按空列表降级渲染,页面不再死给你看。解读——防御的层次有三等:致命参数直接抛错(让调用方立刻发现),可降级参数给默认值(让页面活着),可疑输入留日志(让排查有迹可循)。变式:若这个函数要交给团队外部使用,再加一层配置合并 $.extend(true, {}, defaults, config),安检与补全就都齐了——上一节的 extend 在这里正式与检测工具同台。
上面的插件还可以长成更规范的形态:默认值挂在插件名下,供使用者查阅与整体覆盖——
(function ($) { $.fn.tag.defaults = { cls: 'tagged', scope: 'closest' }; $.fn.tag = function (options) { var opts = $.extend({}, $.fn.tag.defaults, options); return this.each(function () { var host = opts.scope === 'self' ? $(this) : $(this).parent(); host.children().addClass(opts.cls); }); }; })(jQuery); $.fn.tag.defaults.cls = 'picked'; // 全局改默认 $('.row').tag({ scope: 'self' }); // 单次覆盖
这个形态的好处是"默认值成为公开文档":使用者一眼能查到全部可调项,也能一次改掉全站默认,而不必每次调用都传全套参数。对照主流插件的配置写法(传一个 options 对象、暴露 defaults、支持深合并),套路如出一辙——读懂这一个,就读懂了那个生态的配置约定。
| 检测需求 | jQuery 写法 | 原生等价物 | 备注 |
|---|---|---|---|
| 纯对象判断 | $.isPlainObject |
Object.getPrototypeOf(x) === Object.prototype 近似 |
原生没有一刀切的现成函数 |
| 函数判断 | $.isFunction |
typeof x === 'function' |
完全等价 |
| 数字判断 | $.isNumeric |
typeof x === 'number' && !isNaN(x) |
jQuery 认数字字符串,语义不同 |
| 空对象判断 | $.isEmptyObject |
Object.keys(x).length === 0 |
等价,原生更直白 |
| 数组判断 | $.isArray |
Array.isArray |
jQuery 3 已是别名 |
| 节点包含 | $.contains |
Node.prototype.contains |
同名同义 |
| 类型名 | $.type(已废弃) |
Object.prototype.toString.call |
原生组合拳接班 |
这张表的读法:大多数检测已被原生覆盖,老代码迁移时逐行对号;真正值得一提的是 isNumeric 这类"语义型"检测——它替你做了字符串到数字的宽容判断,用原生替代时要决定要不要保留这份宽容,别让迁移悄悄改变行为。
isNumeric 认数字字符串、type 已废弃,新代码用 typeof 组合拳。$.browser 是反面教材。$.contains 与原生 Node.contains 同名同义,判定包含关系。return this 保链式。仪表与接口点验完毕。下一节进性能车间:四个高频止血点,和一份写代码时该绕开的反面清单。