概览
随着浏览器标准的统一和原生 JavaScript API的日益强大,许多曾经依赖 jQuery 的功能现在可以直接用原生代码实现。这并不意味着 jQuery 不再有价值,而是在现代项目中选择合适的技术时,了解原生替代方案有助于精简代码、减少依赖并提升性能。本章旨在提供一个 从jQuery到现代原生JavaScript的替代方案 的参考,涵盖最常见的操作场景,通过对比帮助开发者做出更明智的技术决策。
选择器与DOM查询
jQuery 最深入人心的功能之一是其强大的选择器引擎 $()。在现代浏览器中,document.querySelector() 和 document.querySelectorAll() 提供了几乎同等的能力。
语法
// jQuery
var $elements = $('.my-class');
// 原生 JavaScript
var elements = document.querySelectorAll('.my-class');querySelectorAll 返回一个静态的 NodeList,而非 jQuery 集合对象,因此不包含链式调用的方法。但它支持所有标准CSS选择器,足以应对绝大多数查询需求。
示例:选择器对比
以下示例展示了使用 jQuery 和原生方法进行相同DOM查询的操作。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>原生JavaScript替代方案示例:选择器</title>
</head>
<body>
<div class="container">
<ul id="list">
<li class="item">项目1</li>
<li class="item">项目2</li>
<li class="item special">项目3(特殊)</li>
</ul>
</div>
<script>
// 原生 JavaScript 实现
// 1. 通过 ID 获取元素
const list = document.getElementById('list');
console.log('原生 - ID:', list);
// 2. 通过类名获取所有元素
const items = document.querySelectorAll('.item');
console.log('原生 - 类名 (.item) 数量:', items.length);
// 3. 获取复合选择器
const specialItem = document.querySelector('.item.special');
console.log('原生 - 复合选择器 (.item.special):', specialItem.textContent);
// 4. 遍历 NodeList
items.forEach(item => {
console.log('原生 - 遍历项目:', item.textContent);
});
/* jQuery 对比写法 (假设已加载 jQuery)
$(document).ready(function() {
console.log('jQuery - ID:', $('#list').get(0));
console.log('jQuery - 类名数量:', $('.item').length);
console.log('jQuery - 复合选择器:', $('.item.special').text());
$('.item').each(function() {
console.log('jQuery - 遍历项目:', $(this).text());
});
});
*/
</script>
</body>
</html>DOM操作
jQuery 简化了DOM元素的创建、插入、修改和移除。原生 JavaScript 同样提供了完整的方法。
创建与插入元素
语法
// jQuery
var $newDiv = $('<div>').text('Hello').addClass('my-div');
$('body').append($newDiv);
// 原生 JavaScript
var newDiv = document.createElement('div');
newDiv.textContent = 'Hello';
newDiv.classList.add('my-div');
document.body.appendChild(newDiv);修改内容与属性
语法
// jQuery
$('#my-link').attr('href', 'https://example.com').text('示例链接');
// 原生 JavaScript
var link = document.getElementById('my-link');
link.setAttribute('href', 'https://example.com');
link.textContent = '示例链接';移除元素
语法
// jQuery
$('#my-element').remove();
// 原生 JavaScript
var element = document.getElementById('my-element');
element.remove(); // 现代浏览器
// 或 element.parentNode.removeChild(element); // 兼容旧版示例:DOM操作对比
以下示例对比了使用 jQuery 和原生方法进行一系列DOM操作的代码。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>原生JavaScript替代方案示例:DOM操作</title>
</head>
<body>
<div id="app">
<p class="old">旧段落</p>
</div>
<button id="run-native">运行原生操作</button>
<!-- <button id="run-jquery">运行jQuery操作 (需加载jQuery)</button> -->
<script>
document.getElementById('run-native').addEventListener('click', function() {
const app = document.getElementById('app');
// 1. 创建新元素
const newParagraph = document.createElement('p');
newParagraph.textContent = '这是一个新段落';
newParagraph.classList.add('new');
// 2. 插入元素
app.appendChild(newParagraph);
// 3. 修改现有元素
const oldParagraph = document.querySelector('.old');
if (oldParagraph) {
oldParagraph.innerHTML = '旧段落已被<strong>修改</strong>';
oldParagraph.style.color = 'blue';
}
// 4. 删除元素 (删除刚添加的新段落)
// newParagraph.remove(); // 现代方式
if (newParagraph.parentNode) {
newParagraph.parentNode.removeChild(newParagraph); // 兼容方式
}
// 5. 操作属性
const button = document.getElementById('run-native');
button.setAttribute('data-status', 'clicked');
button.disabled = true; // 直接操作属性
});
/* jQuery 版本对比
$('#run-jquery').on('click', function() {
var $app = $('#app');
// 1. 创建新元素并插入
$('<p>', {
text: '这是一个新段落',
class: 'new'
}).appendTo($app);
// 2. 修改现有元素
$('.old').html('旧段落已被<strong>修改</strong>').css('color', 'blue');
// 3. 删除新添加的元素
$app.find('.new').remove();
// 4. 操作属性
$('#run-jquery').attr('data-status', 'clicked').prop('disabled', true);
});
*/
</script>
</body>
</html>事件处理
jQuery 的 .on() 方法提供了统一的事件绑定接口。原生 JavaScript 使用 addEventListener 同样强大且灵活。
语法
// jQuery
$('#my-button').on('click', function(e) {
console.log('按钮被点击', e.target);
});
// 原生 JavaScript
document.getElementById('my-button').addEventListener('click', function(e) {
console.log('按钮被点击', e.target);
});事件委托
jQuery 的事件委托十分简洁。原生实现同样利用事件冒泡。
语法
// jQuery - 事件委托
$('#parent-list').on('click', 'li', function() {
console.log('列表项被点击');
});
// 原生 JavaScript - 事件委托
document.getElementById('parent-list').addEventListener('click', function(e) {
if (e.target && e.target.matches('li')) {
console.log('列表项被点击');
}
});示例:事件处理对比
以下示例展示了事件绑定和事件委托在两种技术中的实现。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>原生JavaScript替代方案示例:事件处理</title>
</head>
<body>
<ul id="todo-list">
<li>学习原生API</li>
<li>理解事件委托</li>
</ul>
<button id="add-item">添加待办</button>
<script>
const list = document.getElementById('todo-list');
const addBtn = document.getElementById('add-item');
// 原生事件委托:监听父元素上的点击事件
list.addEventListener('click', function(e) {
if (e.target && e.target.tagName === 'LI') {
e.target.style.textDecoration = 'line-through';
console.log('完成事项:', e.target.textContent);
}
});
// 原生事件绑定:添加新项
addBtn.addEventListener('click', function() {
const newItem = document.createElement('li');
newItem.textContent = '新事项 ' + (list.children.length + 1);
list.appendChild(newItem);
});
/* jQuery 版本对比
$('#todo-list').on('click', 'li', function() {
$(this).css('text-decoration', 'line-through');
console.log('完成事项:', $(this).text());
});
$('#add-item').on('click', function() {
$('#todo-list').append('<li>新事项 ' + ($('#todo-list li').length + 1) + '</li>');
});
*/
</script>
</body>
</html>Ajax请求
jQuery 的 $.ajax 和便捷方法简化了异步请求。现代 JavaScript 提供了 fetch API,基于Promise设计,更加强大和灵活。
语法
// jQuery
$.ajax({
url: '/api/data',
method: 'GET',
success: function(data) {
console.log(data);
},
error: function(error) {
console.error(error);
}
});
// 原生 JavaScript (fetch)
fetch('/api/data')
.then(response => {
if (!response.ok) {
throw new Error('网络响应异常');
}
return response.json(); // 或 response.text() 等
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('请求失败:', error);
});示例:Ajax请求对比
以下示例使用 fetch 发起GET请求,并处理JSON响应。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>原生JavaScript替代方案示例:Ajax (fetch)</title>
</head>
<body>
<button id="load-data">使用 fetch 加载数据</button>
<pre id="result">等待请求...</pre>
<script>
document.getElementById('load-data').addEventListener('click', function() {
const resultEl = document.getElementById('result');
resultEl.textContent = '请求中...';
// 使用 fetch 发起 GET 请求 (以 GitHub API 为例)
fetch('https://api.github.com/repos/jquery/jquery')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP 错误! 状态: ${response.status}`);
}
return response.json();
})
.then(data => {
resultEl.textContent = JSON.stringify({
仓库: data.name,
描述: data.description,
Star: data.stargazers_count,
Fork: data.forks_count
}, null, 2);
})
.catch(error => {
resultEl.textContent = '请求失败: ' + error.message;
});
});
/* jQuery 版本对比
$('#load-data').on('click', function() {
$('#result').text('请求中...');
$.ajax({
url: 'https://api.github.com/repos/jquery/jquery',
dataType: 'jsonp', // 注意 GitHub API 支持 JSONP 或 CORS
success: function(data) {
$('#result').text(JSON.stringify({
仓库: data.name,
描述: data.description,
Star: data.stargazers_count,
Fork: data.forks_count
}, null, 2));
},
error: function() {
$('#result').text('请求失败');
}
});
});
*/
</script>
</body>
</html>工具函数
jQuery 提供了许多实用工具函数,其中很多已被ES5/ES6纳入标准库。
| jQuery 方法 | 原生 JavaScript 替代方案 | 说明 |
|---|---|---|
$.isArray() | Array.isArray() | jQuery 4.0 已移除,直接使用原生。 |
$.trim() | String.prototype.trim() | jQuery 4.0 已移除,直接调用 str.trim()。 |
$.map() | Array.prototype.map() | 用于数组;对于对象可用 Object.keys() 结合 map()。 |
$.each() | Array.prototype.forEach() 或 for...of 循环 | 遍历数组或类数组对象。 |
$.extend() | 对象展开 { ...obj1, ...obj2 } 或 Object.assign() | 浅拷贝合并对象。 |
$.inArray() | Array.prototype.indexOf() 或 includes() | 检查值在数组中的位置。 |
$.parseJSON() | JSON.parse() | jQuery 4.0 已移除,直接使用原生。 |
$.isNumeric() | !isNaN(parseFloat(n)) && isFinite(n) | jQuery 4.0 已移除,需自行实现或使用 Number.isFinite() (ES6)。 |
$.type() | typeof 结合 Object.prototype.toString.call() | jQuery 4.0 已移除,用于更精确的类型判断。 |
$.now() | Date.now() | jQuery 4.0 已移除,获取当前时间戳。 |
示例:工具函数对比
以下示例展示了几个常用工具的原生实现。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>原生JavaScript替代方案示例:工具函数</title>
</head>
<body>
<script>
// 1. 数组检测
const arr = [1, 2, 3];
console.log('原生 Array.isArray(arr):', Array.isArray(arr)); // true
// 2. 字符串去除两端空白
const str = ' hello world ';
console.log('原生 str.trim():', str.trim()); // "hello world"
// 3. 遍历数组
const items = ['A', 'B', 'C'];
items.forEach((item, index) => {
console.log(`原生 forEach: 索引 ${index} = ${item}`);
});
// 4. 合并对象
const defaults = { color: 'red', size: 10 };
const options = { color: 'blue' };
const settings = Object.assign({}, defaults, options); // 或 { ...defaults, ...options }
console.log('原生 Object.assign:', settings); // { color: 'blue', size: 10 }
// 5. 检测数值
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
console.log('原生 isNumeric("123"):', isNumeric('123')); // true
console.log('原生 isNumeric("12.3"):', isNumeric('12.3')); // true
console.log('原生 isNumeric("abc"):', isNumeric('abc')); // false
// 6. JSON 解析
const jsonStr = '{"name": "jQuery"}';
const parsed = JSON.parse(jsonStr);
console.log('原生 JSON.parse:', parsed.name); // jQuery
/* jQuery 版本对比 (概念性)
console.log('jQuery $.isArray(arr):', $.isArray(arr));
console.log('jQuery $.trim(str):', $.trim(str));
$.each(items, function(index, item) { console.log('jQuery each:', index, item); });
var settings = $.extend({}, defaults, options);
console.log('jQuery $.isNumeric("123"):', $.isNumeric('123'));
var parsed = $.parseJSON(jsonStr);
*/
</script>
</body>
</html>版本变更记录
下表梳理了 jQuery 版本迭代中与原生替代方案相关的关键变化,特别是4.0.0中移除大量API,直接推动了向原生 JavaScript 的迁移。
| 版本 | 变更内容与影响 |
|---|---|
| 1.0 | 提供大量实用工具函数和跨浏览器兼容性处理,在当时原生API匮乏且不统一的背景下成为必需品。 |
| 1.5+ | 引入Deferred,$.ajax 开始返回Promise兼容对象,这些功能后来被原生Promise和 fetch 借鉴。 |
| 2.x | 放弃对IE 6-8的支持,开始采用更现代的原生API,但保留了大部分工具函数以保持兼容性。 |
| 3.x | 进一步优化性能,并逐步弃用一些已有原生替代品的API,为4.0的清理做准备。 |
| 4.0 | 重大变革:移除大量已废弃的API,包括 jQuery.isArray、jQuery.trim、jQuery.type、jQuery.isNumeric、jQuery.isFunction、jQuery.parseJSON 等。官方明确推荐使用原生 Array.isArray()、String.prototype.trim()、JSON.parse() 等替代。此举旨在精简库体积,鼓励开发者拥抱现代 JavaScript 标准。 |
浏览器兼容状态
采用原生 JavaScript 替代方案时,需关注目标浏览器对特定API的支持情况。下表列出了本章涉及的原生API的最低支持版本,可作为项目决策的参考。
| 浏览器 | 最低支持版本(主要API) | 备注 |
|---|---|---|
| Chrome | 30+ | fetch 在 42+ 稳定,NodeList.forEach 在 51+ 支持。 |
| Edge | 12+ | 基于Chromium的Edge(79+)支持更佳。 |
| Firefox | 25+ | fetch 在 39+ 支持,NodeList.forEach 在 50+ 支持。 |
| Opera | 18+ | 基本与Chrome同步。 |
| Safari | 7+ | fetch 在 10.1+ 支持,NodeList.forEach 在 10+ 支持。 |
| Chrome Android | 30+ | 跟随桌面版Chrome。 |
| Firefox for Android | 25+ | 跟随桌面版Firefox。 |
| Opera Android | 18+ | 跟随桌面版Opera。 |
| Safari on iOS | 7+ | fetch 在 iOS 10.3+ 支持。 |
| Samsung Internet | 4.0+ | 基于Chromium,支持度较好。 |
| WebView Android | 4.4+ | fetch 在 5.0+ (WebView 60+) 支持。 |
| WebView on iOS | 7+ | 与 iOS Safari 一致。 |
注:对于需要支持较旧浏览器(如IE)的项目,仍需考虑使用 jQuery 或引入polyfill。但对于现代浏览器环境,原生 JavaScript 已成为可靠且高效的替代方案。理解 jQuery 的设计思想与原生API的对应关系,有助于编写更简洁、更贴近标准的代码。
