JavaScript与jQuery实战:从原生基础到常见坑与待办案例

说实话,每次带新人,都会被同一个问题问到:现在前端都 Vue、React 了,还有必要花时间学 jQuery 吗?前两天朋友想加个网站动态效果,让我帮他改造一下页面,我看了一眼他的项目,Bootstrap + jQuery 的老搭档,顺手就用起来,几分钟把滑动门菜单和一个弹窗模块搞定了。他愣了半天,然后说了一句:"这玩意儿还没死啊?"

我反手给他看了几个正在线上跑的项目,然后问他:如果有一天你要接手其中任何一个,你会怎么办?前端这个行业走到今天,HTML 和 CSS 负责骨架和皮肤,JavaScript 负责肌肉和神经,而 jQuery 曾经是调用肌肉最省力的那根神经。哪怕到了现在,这套"HTML + CSS + JavaScript + jQuery"的组合拳,在存量项目、快速原型、后端顺手做页面这些场景里,依然活得很好。

这篇是这个快速入门系列的第三篇,前两篇我们打通了 HTML 标签语义和 CSS 布局样式,这一篇正式来啃 JavaScript,同时把 jQuery 这个老牌工具放进实战里去用。内容会从变量、函数、数组、DOM 这些 JavaScript 地基,延伸到选择器、事件委托、链式操作这些 jQuery 日常,再结合真实项目里常见的报错和坑,最后用一个完整的待办事项案例带你走一遍 JS 和 jQuery 双版本实现。不管你是后端顺手写页面,还是准备正式入行前端,看完这篇再动手敲一遍,你手里的家伙就齐了。

1. 先回答一个现实问题:2025 年了,学 jQuery 到底图什么

1.1 我见过 jQuery 最多的场合

如果只看招聘网站的岗位要求,jQuery 几乎可以忽略不计。但如果你真正接触过中小公司的线上项目,你会发现它压根没消失。

我这些年接手过的项目里,jQuery 出现最多的场合有这么几类:

  • 企业后台管理系统,尤其是一批基于 Bootstrap + jQuery 搭起来的老后台,表格、弹窗、表单校验、权限菜单全部跑在 jQuery 上。
  • 服务端渲染的页面,后端程序员自己维护的管理界面,图省事直接引 jQuery,交互逻辑用几行 $() 搞定。
  • 官网、活动页、信息展示站,不需要工程化构建,打开浏览器就能跑,jQuery 插件随便挑。
  • 各种开源插件生态,像轮播图、日期选择、富文本编辑器、数据表格,老牌插件底层基本都是 jQuery 写的。

前两年帮朋友维护一个公司内部后台,整个项目没有打包工具、没有前端框架,清一色静态 HTML + jQuery + Bootstrap。每次改需求,就是改选择器、调 AJAX、改 DOM 结构。这个后台承担了全公司每天上千次的数据录入和审核操作,谁要跟老板说"这东西该重构了",老板只会问一句:重构期间谁保证系统不停?

这就是 jQuery 真实的生存状态。

1.2 jQuery 本质上是一套"操作 DOM 的哲学"

我经常跟新人强调:jQuery 不是另一门语言,它是对原生 JavaScript 中 DOM 操作、事件处理、AJAX 这些重复劳动做的一层封装。它的核心套路就两件事:找到元素、操作元素。然后换成一条链写出来。

这就是 jQuery 标志性的链式操作。同样是拿到一个元素,加个样式,再改属性,原生 JS 要写好几句,jQuery 可以一路点下去。这套"查找元素 + 操作元素"的命令式思维,和现在框架里的"数据驱动"完全不同,但你不能说它没用。

有三个核心思想,即便到今天依然值得从 jQuery 里吸收:

  • 选择器语法:jQuery 里拿 CSS 选择器查元素的写法,后来原生 API querySelectorAll 直接借鉴了同一套规则。
  • 链式调用:方法返回自身、继续调下一个方法的设计,现在很多库的 API 依然长这样。
  • 事件委托:把事件绑定到父节点,通过判断目标元素统一处理,React 的合成事件底层思路和它是同源的。

把这套思想看明白了,你会发现自己学 Vue、React 并不是从零开始,很多概念早就在 jQuery 里见过。

1.3 我给初学者的一条学习路线

关于"要不要学 jQuery",我的建议不是简单的要或不要,而是讲究顺序:

第一步,原生 JavaScript 必须先过一遍基础,变量、函数、数组、对象、DOM 增删改查、事件绑定与冒泡、AJAX 请求,这些跑不掉。第二步,用 jQuery 实现同样的功能,感受一下代码量怎么从十行变三行,同时深入了解它为什么能做到。第三步,带着 jQuery 里的选择器思维、事件委托思维去学现代框架,你会发现很多概念是相通的。

我自己带人的时候,一直坚持一个原则:先原生,后 jQuery,最后再上框架。跳过原生直接学 jQuery 的人,遇到报错往往一脸懵,因为他不清楚底层在干什么;反过来,原生基础扎实的人,学 jQuery 基本一天就能上手干活。

需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。

2. 原生 JavaScript 的地基:变量、函数、数组与 DOM 操作

jQuery 用得再熟,也建议先把原生 JavaScript 过一遍。原因很简单,jQuery 说到底就是一层封装,你连封装底下是什么都不知道,出了 Bug 就只能干瞪眼。

2.1 变量声明与数据类型

JavaScript 声明变量有 var、let、const 三种方式。面试里最常考的区别是 var 有变量提升和函数作用域,let 和 const 支持块级作用域。但在实战里,我给你的建议更简单粗暴:能 const 就不要 let,能 let 就不要 var。

javascript复制const PI = 3.14;        // 常量,声明后不可重新赋值
let count = 0;          // 可变变量,块级作用域
count = count + 1;      // 正常赋值
var old = '我不建议用'; // var 函数作用域,容易出乱子

数据类型方面,JS 里有 number、string、boolean、null、undefined、object、symbol、bigint 几种基础类型。新手最容易踩的坑是 typeof null 返回的是 "object",这是 JS 诞生时遗留的 Bug,官方选择保留而不是修复。遇到判断空值的情况,别光用 typeof,要结合 === null 一起判断。

2.2 函数是 JavaScript 里的一等公民

函数在 JavaScript 里有三种常见的写法:函数声明、函数表达式、箭头函数。

javascript复制// 函数声明:会提升,可以在声明之前调用
function add(a, b) {
  return a + b;
}

// 函数表达式:不会提升,必须声明后才能使用
const subtract = function(a, b) {
  return a - b;
};

// 箭头函数:写法更简洁,但没有自己的 this
const multiply = (a, b) => a * b;

箭头函数在事件回调里要格外小心。jQuery 里如果用了箭头函数,内部 this 指向会和 function 写法不同。举个真实例子,$('#btn').on('click', () => { $(this)... }) 这里的 this 就不再指向按钮元素,而是指向外层上下文,新手经常在这里翻车。所以你在 jQuery 事件回调里,要么用 function 关键字,要么用 $(event.target) 替代 this。

2.3 数组的常见操作与"判断有没有数据"

数组在 JS 里太常用了。先看几个增删方法:

javascript复制const arr = [1, 2, 3];
arr.push(4);      // 末尾添加 -> [1, 2, 3, 4]
arr.pop();        // 删除末尾 -> [1, 2, 3]
arr.unshift(0);   // 开头添加 -> [0, 1, 2, 3]
arr.shift();      // 删除开头 -> [1, 2, 3]

还有一个高频操作:往一个空数组里添加数据之后,判断数组里到底有没有值。这个问题我在热搜里看到过,"jquery var arr=[] 添加 判断arr有没有数据"。最标准的答案永远是 length 属性:

javascript复制const list = [];
list.push('任务一');
list.push('任务二');

if (list.length > 0) {
  console.log('数组里有数据,共 ' + list.length + ' 条');
} else {
  console.log('数组是空的');
}

这个判断在 jQuery 和原生 JS 里都通用。还有两个常用的遍历方法 forEach 和 map,前者只管遍历,后者会返回一个新数组。需要过滤数据用 filter,需要把数组变成字符串用 join,这几个方法掌握了,日常开发基本够用。

2.4 浏览器里的 DOM 操作

浏览器把我们写的 HTML 解析成一棵节点树,JavaScript 可以在树上查找、添加、修改、删除节点。这套操作统称为 DOM 操作。

新版本浏览器推荐用下面这套组合:

javascript复制// 查找元素
const box = document.getElementById('box');       // 根据 id 找
const items = document.querySelectorAll('.item'); // 根据 CSS 选择器找
const btn = document.querySelector('#btn');       // 找单个元素

// 创建元素
const div = document.createElement('div');
div.textContent = '我是一段文本';
div.className = 'card';

// 插入元素
const container = document.getElementById('container');
container.appendChild(div);

// 修改样式和属性
div.style.backgroundColor = '#f5f5f5';
div.setAttribute('data-id', '1001');

这套操作虽然啰嗦,但是必须会。jQuery 做的事,本质上就是把上面这些步骤封装成 $() 和 .append(),让你少打几个字。

2.5 事件绑定、事件冒泡与事件委托

浏览器交互的核心是事件。比如用户点击按钮,触发 click 事件。原生 JS 绑定事件的标准写法是 addEventListener:

javascript复制const btn = document.getElementById('btn');
btn.addEventListener('click', function(event) {
  console.log('按钮被点击了');
  console.log(event.target); // 当前点击的目标元素
});

事件有一个重要特性叫冒泡:点击子元素时,click 事件会从触发它的最深层节点,一层一层向上传播到父节点。基于冒泡机制,我们可以做事件委托——只在父节点上绑定一次事件,所有子节点的事件都交给它统一处理。

javascript复制const list = document.getElementById('list');

// 事件委托:父节点统一监听
list.addEventListener('click', function(event) {
  const target = event.target;
  if (target.classList.contains('delete-btn')) {
    console.log('点击了删除按钮');
  }
});

事件委托还有一个好处:动态添加进去的新子元素,不需要重新绑定事件,一样能被父节点监听到。这个思想在后文 jQuery 实战里会反复用到。

3. jQuery 实战:选择器、创建标签与事件委托

这一章我们正式进入 jQuery。先说个前提:你不用把 jQuery 的每个 API 背下来,只需要掌握选择器、DOM 操作、事件绑定、AJAX 这几大块,剩下的大多是查文档的事。

3.1 引入 jQuery 的两种方式

第一种是 CDN 引入,适合联网环境:

html复制<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

第二种是下载 jQuery 文件放到本地项目里,通过相对路径引入:

html复制<script src="js/jquery.min.js"></script>

版本选择上有个实际经验:如果是新项目,直接用 3.x 最新版;如果是要维护老项目,对方用的 1.x 千万别随手升级,老项目里很多插件在 2.x、3.x 下会出兼容性问题。1.x 版本的 jQuery 对 IE6/7/8 都有兼容,这在现在基本用不上了,但老项目里跑得稳,就别乱动。

3.2 选择器与链式操作

jQuery 的核心入口是 $ 函数。传一个 CSS 选择器进去,返回的是一个 jQuery 对象,这个对象上挂了一堆操作方法。

javascript复制$('#btn')               // 选 id 为 btn 的元素
$('.card')              // 选所有 class 为 card 的元素
$('li.item')            // 选 li 且 class 为 item 的元素
$('input[name="name"]') // 选 name 属性为 name 的 input

链式操作是 jQuery 最爽的地方。每个操作元素的方法执行完会返回当前 jQuery 对象本身,所以可以一路点下去:

javascript复制$('#myBtn')
  .addClass('active')                // 加样式类
  .css('font-size', '16px')          // 改内联样式
  .attr('data-status', 'enabled')    // 设置属性
  .text('按钮已启用');                 // 改文本内容

对比一下原生写法,这四步至少四行代码,jQuery 一行搞定。这就是"少打代码"最直观的体现。

3.3 创建标签与插入内容

有朋友搜过"jquery 创建一个标签",确实创建动态内容是前端高频操作。原生 JS 用 createElement,jQuery 更简洁,直接往 $() 里传 HTML 字符串:

javascript复制// 创建一个 li 标签,带 class 和文本
const $li = $('<li>', {
  class: 'todo-item',
  text: '这是一个新任务',
  'data-id': 5
});

// 也可以直接写 HTML 字符串
const $li2 = $('<li class="todo-item" data-id="5">这是一个新任务</li>');

创建之后要插入到页面里,常用方法有 append、prepend、after、before:

javascript复制$('#todoList').append($li);   // 添加到列表末尾
$('#todoList').prepend($li2); // 添加到列表开头
$('#todoList').after(其他元素); // 插到列表后面

这里有一个必须强调的安全习惯:如果 HTML 字符串里有用户输入的数据,一律不要直接用 $() 拼 HTML,用 text() 方案或者先做转义。这个坑在第四章专门讲。

3.4 text() 和 html() 的区别

text() 拿的是纯文本,html() 拿的是内部 HTML 结构。反过来说,用 text() 设置内容,内容会被当作纯文本渲染;用 html() 设置内容,内容会作为 HTML 解析。

有个实际场景:判断一段文本里有没有换行符。如果是通过 text() 获取的文本,换行符是 \n 字符;如果是通过 html() 获取的内容,换行可能显示成
标签。所以判断时要分情况:

javascript复制const content = $('#msg').text();
if (content.indexOf('\n') !== -1) {
  console.log('文本中包含换行符');
}

const contentHtml = $('#msg').html();
if (contentHtml.indexOf('<br') !== -1) {
  console.log('内容中有 br 换行标签');
}

这个细节处理不好,很多字符串判断逻辑就会神秘失效。我在真实项目里被这个坑绊过一次,后来凡是涉及"判断内容里有没有换行",都先明确我用的是 text() 还是 html()。

3.5 事件绑定的两种姿势与事件委托

jQuery 里普通事件绑定可以用 .on() 方法:

javascript复制$('#btn').on('click', function() {
  console.log('按钮被点击');
});

// 等价于
$('#btn').click(function() {
  console.log('按钮被点击');
});

但如果是动态生成的元素,直接对它绑事件大概率无效。比如你运行了 $('.delete-btn').on('click', ...),但 .delete-btn 是后面才加进页面的,这时绑定失败。

正确姿势是把事件委托给一个始终存在的父容器:

javascript复制$('#todoList').on('click', '.delete-btn', function() {
  // this 指向被点击的 .delete-btn 元素
  $(this).closest('li').remove();
});

这种写法的意思是:我监听父容器 #todoList 的 click 事件,但只有当点击目标匹配 .delete-btn 时,这个回调才会执行。无论这个元素是什么时候添加到页面里的,事件都能被捕获。这个用法是 jQuery 实战里最重要的一个,没有之一。

3.6 遍历和判断数组数据

jQuery 也提供了遍历工具 $.each()。对数组和对象都适用:

javascript复制const arr = [];
arr.push('A');
arr.push('B');

$.each(arr, function(index, item) {
  console.log(index, item);
});

// 遍历 jQuery 对象集合
$('.card').each(function() {
  console.log($(this).text());
});

配合前面说过的判断逻辑,经常这样用:

javascript复制const selectedIds = [];
$('.checkbox:checked').each(function() {
  selectedIds.push($(this).val());
});

if (selectedIds.length > 0) {
  // 说明有选中项,发请求或做其他处理
}

数组空判断依然是 length,这条建议刻进肌肉记忆。另外 $.isArray() 可以判断一个变量是不是数组,$.trim() 可以去掉字符串首尾空格,这些工具方法很常用,建议翻一眼文档。

3.7 AJAX 与常用工具方法

用 jQuery 发 AJAX 请求,比原生 fetch 和 XMLHttpRequest 更省心,基本就是给个配置对象:

javascript复制$.ajax({
  url: '/api/todo/list',
  method: 'GET',
  dataType: 'json'
})
.done(function(res) {
  console.log('请求成功', res);
})
.fail(function() {
  console.log('请求失败');
});

简化写法还有 $.get()、$.post()。不过要提醒一句:如果你的新项目注定要用框架,AJAX 这块直接用 fetch 或者 axios 更合适;jQuery 的 AJAX 主要是兼容老项目时的必需品。

4. 真实项目里绕不开的坑:运行时报错、XSS 与事件陷阱

这个系列前两篇讲 HTML 和 CSS 时,我反复强调过"知道怎么排查"比"知道怎么用"更重要。JavaScript 和 jQuery 也一样,下面这几个问题和场景,是我在真实项目里遇到最多、也最值得新手花时间弄懂的。

4.1 排查 JavaScript 运行时报错的完整思路

有朋友搜过"javascript运行时报错"这类问题。JS 的报错比 Java、C++ 友好得多,因为它本身就是解释执行,浏览器会把错误信息直接打在控制台里。我一般按下面这个流程排查:

第一步,看报错信息。最常见的几类:

  • SyntaxError:语法错误,写了不合法的 JS 代码,比如括号没闭合、字符串少引号。
  • ReferenceError:引用了不存在的变量,比如拼错了名字,搞错了作用域。
  • TypeError: Cannot read property 'xxx' of null / undefined:访问了 null 或 undefined 对象的属性。这是实践中最常见的报错,大多因为获取的 DOM 元素不存在,或者接口数据没返回对应字段。

第二步,看报错文件位置和行号。浏览器控制台会把报错信息定位到某个.js文件的某一行,点击就能跳转到 Sources 面板。如果是压缩过的代码,根本看不懂,这时就需要用 Source Map 还原源码。

第三步,加 console.log 缩小范围。在报错位置前面几行打印关键变量,确认它到底是谁、值是什么。

举个例子,新手最常见的报错:

javascript复制const btn = document.getElementById('submitBtn');
btn.addEventListener('click', handler); // 报错:btn 为 null

为什么 btn 是 null?多半是 JS 放在 head 里,执行时浏览器还没解析到按钮,getElementById 当然找不到。解决办法有两个:把 script 标签放到 body 底部,或者把代码包进 DOMContentLoaded 事件里。jQuery 写法就是经典的:

javascript复制$(function() {
  // 在这里写代码,等 DOM 就绪后再执行
});

这个排查流程看着简单,但能解决掉日常至少七成的 TypeError。不要一报错就去问人,先看报错信息、再看定位、再打日志,绝大多数问题能自己解决。

4.2 "点击元素消失,点击元素内按钮不消失"到底是怎么回事

这个需求非常常见:页面上有个弹窗或下拉面板,点弹窗外部区域,弹窗关闭;但点弹窗内部的某个按钮,按钮的点击行为要生效,弹窗不能关。很多新手写出来发现,点弹窗里面的按钮,弹窗也跟着关了,怎么点都不对。

原因就是事件冒泡。点击按钮时,事件从按钮一路冒泡到弹窗、再到 body、再到 document。如果你在 document 上监听 click 来"点击外部关闭弹窗",那点按钮时,事件也会冒泡到 document,于是"关闭"逻辑被触发了。

解决方式有两种。

第一种,在弹窗内部元素的点击事件里阻止冒泡:

javascript复制$('.modal').on('click', '.modal-close-btn', function(event) {
  event.stopPropagation(); // 阻止事件继续向外冒泡
  // 做关闭按钮自己的事情
});

$(document).on('click', function() {
  $('.modal').hide(); // 点击外部隐藏弹窗
});

第二种,在 document 的监听器里判断点击目标是否在弹窗内:

javascript复制$(document).on('click', function(event) {
  const $target = $(event.target);
  if ($target.closest('.modal').length === 0) {
    $('.modal').hide(); // 点的不在弹窗内,才隐藏
  }
});

第二种写法更稳定,因为它不依赖弹窗内部元素是否都调用了 stopPropagation。我用这种方案处理过很多弹窗关闭需求,推荐优先用它。类似的场景还有下拉菜单、右键菜单、通知气泡,逻辑完全一样。

4.3 jQuery 的 XSS 风险:html()、append() 和 text()

"jquery xss"是很多人搜过的关键词。jQuery 确实有 XSS 风险,但风险不在 jQuery 本身,而在使用方式。核心规则一句话:永远不要把用户输入的内容直接塞进 html() 或 append() 里。

举例,一个评论列表,用户提交的内容是 ,如果前端把这条内容直接当作 HTML 插入页面,浏览器会尝试执行脚本,这就是 XSS 攻击。换到 jQuery 上:

javascript复制// 危险:会把用户内容当成 HTML 解析
$('#commentList').append('<div class="comment">' + userInput + '</div>');

// 安全:当作纯文本插入
const $comment = $('<div>', { class: 'comment', text: userInput });
$('#commentList').append($comment);

text() 会把内容里的特殊字符转义成实体,浏览器只会显示文本,不会解析成标签或脚本。所以安全原则是:

  • 想插入纯文本,用 text()。
  • 必须插入 HTML 结构时,先确认数据源可信。如果数据来自用户,你要先做 HTML 转义,把 < 转成 <、> 转成 >。
  • 不要在字符串拼接里直接塞未过滤的用户数据,不管它是不是 jQuery。

4.4 javascript:void(0) 的来历和陷阱

很多人写 a 标签跳转占位的时候,喜欢写 href="javascript:void(0)"。为什么这么写?因为 a 标签默认有跳转行为,如果不给 href,样式上不像链接;如果给了 #,点击后页面会跳到顶部,体验很怪。javascript:void(0) 的意思就是执行一个返回 undefined 的表达式,让点击后什么也不发生。

但这个写法有几个问题:

第一,把 JS 逻辑写进 HTML,违反了行为和结构分离的原则。第二,如果写成 javascript:void(o),把数字 0 错打成字母 o,浏览器会报错"o is not defined",我在热搜里看到有人问"javascript:void(o)报错,为什么",这就是典型的笔误。第三,将 href 和 onClick 混在一起,如果 JS 报错,a 标签默认跳转可能还会执行。

我的建议是,尽量别用 javascript:void(0)。如果要做一个按钮样式的点击元素,直接改用 button 标签;如果非用 a 标签不可,可以这样写:

html复制<a href="#" id="noopLink">点击</a>

然后在 JS 里阻止默认行为:

javascript复制$('#noopLink').on('click', function(event) {
  event.preventDefault(); // 阻止 href="#" 跳转到顶部
  // 执行你的逻辑
});

这样行为清晰、不坑人,也方便后来维护的人看懂。

4.5 顺带一提:CSS Flex 布局与 JS 的配合

搜热词列表里还有一条"css flex布局子元素宽度自适应"。这虽然不是 JS 的内容,但前端实战里经常和 JS 搭在一起,简单提一句。

Flex 布局让子元素宽度可以自适应容器,这在响应式页面里非常方便。但有个坑是:当你在 JS 里获取元素宽度时,比如用 offsetWidth、clientWidth、getBoundingClientRect(),拿到的往往是当前渲染宽度。如果元素宽度受 flex 撑开的,同时页面里还有动画、缩放、字体加载等操作,可能会出现"图还没完全加载完,JS 已经拿到了错误的宽度"这种问题。

解决方法是:等图片或者字体资源加载完成后再去取宽度。jQuery 里可以用 $(window).on('load', ...) 或者图片的 load 事件来兜底。这个坑比较细,先记住"取值前确认布局已经稳定"这个原则就行。

5. 综合实战:用原生 JS 和 jQuery 各写一遍"待办事项"

前几章的知识点分散在各个场景里,现在我们把它们串起来。我选择一个非常经典的"待办事项"小工具来做完整案例:输入框填入任务,点击添加,任务进入列表;点击任务前的复选框,任务标记为已完成;点击删除按钮,任务从列表移除。

5.1 功能拆解与准备工作

在写代码之前,先把功能拆成几个动作:

  1. 用户输入内容,点击"添加"按钮,列表新增一条任务。
  2. 新任务里有一个复选框,一个文本,一个删除按钮。
  3. 复选框变化时,任务文字显示删除线。
  4. 删除按钮点击,移除整条任务。
  5. 动态添加的任务依然能触发事件,所以事件要委托。

HTML 骨架很简单,一个输入框、一个按钮、一个列表:

html复制<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="utf-8">
  <title>待办事项</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="todo-app">
    <h1>我的待办</h1>
    <div class="input-row">
      <input type="text" id="todoInput" placeholder="请输入任务">
      <button id="addBtn">添加</button>
    </div>
    <ul id="todoList"></ul>
  </div>
  <script src="app.js"></script>
</body>
</html>

CSS 简单写一点,保证能看清状态变化:

css复制body { font-family: "PingFang SC", "Microsoft YaHei", sans-serif; background: #f5f5f5; }
.todo-app { max-width: 400px; margin: 50px auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.input-row { display: flex; gap: 10px; margin-bottom: 20px; }
#todoInput { flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
#addBtn { padding: 8px 16px; background: #4a90d9; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
#todoList { list-style: none; padding: 0; }
.todo-item { display: flex; align-items: center; gap: 10px; padding: 8px; border-bottom: 1px solid #eee; }
.todo-item .text { flex: 1; }
.todo-item.done .text { text-decoration: line-through; color: #999; }
.delete-btn { background: #e74c3c; color: #fff; border: none; padding: 4px 8px; border-radius: 4px; cursor: pointer; }

5.2 原生 JavaScript 版本

原生版本的逻辑完全按照前面讲的事件委托和第二、三章的知识来:

javascript复制(function() {
  const input = document.getElementById('todoInput');
  const addBtn = document.getElementById('addBtn');
  const list = document.getElementById('todoList');

  // 添加任务函数
  function addTodo() {
    const text = input.value.trim();
    if (!text) {
      alert('请输入任务内容');
      return;
    }

    // 创建 li 元素
    const li = document.createElement('li');
    li.className = 'todo-item';

    const checkbox = document.createElement('input');
    checkbox.type = 'checkbox';
    checkbox.className = 'checkbox';

    const span = document.createElement('span');
    span.className = 'text';
    span.textContent = text;

    const deleteBtn = document.createElement('button');
    deleteBtn.className = 'delete-btn';
    deleteBtn.textContent = '删除';

    li.appendChild(checkbox);
    li.appendChild(span);
    li.appendChild(deleteBtn);

    list.appendChild(li);
    input.value = '';
    input.focus();
  }

  // 给添加按钮绑定事件
  addBtn.addEventListener('click', addTodo);

  // 回车键也能添加
  input.addEventListener('keydown', function(event) {
    if (event.key === 'Enter') {
      addTodo();
    }
  });

  // 事件委托:监听列表里的复选框和删除按钮
  list.addEventListener('change', function(event) {
    if (event.target.classList.contains('checkbox')) {
      event.target.parentElement.classList.toggle('done');
    }
  });

  list.addEventListener('click', function(event) {
    if (event.target.classList.contains('delete-btn')) {
      const li = event.target.parentElement;
      list.removeChild(li);
    }
  });
})();

这里面用到了一个细节:删除按钮是点击事件,复选框是 change 事件,所以分别委托给 list 监听。动态添加的 li 不需要单独绑事件,所有事件都通过 list 统一处理。

5.3 jQuery 版本

同一个功能,用 jQuery 写出来是这样:

javascript复制$(function() {
  const $input = $('#todoInput');
  const $addBtn = $('#addBtn');
  const $list = $('#todoList');

  function addTodo() {
    const text = $input.val().trim();
    if (!text) {
      alert('请输入任务内容');
      return;
    }

    // 创建 li 并一次性插入
    const $li = $(
      '<li class="todo-item">' +
        '<input type="checkbox" class="checkbox">' +
        '<span class="text"></span>' +
        '<button class="delete-btn">删除</button>' +
      '</li>'
    );

    // 用 text() 写入用户输入,防止脚本注入
    $li.find('.text').text(text);
    $list.append($li);

    $input.val('');
    $input.focus();
  }

  $addBtn.on('click', addTodo);

  $input.on('keydown', function(event) {
    if (event.key === 'Enter') {
      addTodo();
    }
  });

  // 事件委托:动态生成的元素也能被监听
  $list.on('change', '.checkbox', function() {
    $(this).closest('li').toggleClass('done');
  });

  $list.on('click', '.delete-btn', function() {
    $(this).closest('li').remove();
  });
});

注意我在生成 li 时用了字符串拼接,但用户输入的内容是通过 $li.find('.text').text(text) 写入的,没有直接拼到 HTML 字符串里。这就是第 4.3 节强调的安全实践。

5.4 两个版本对比:jQuery 到底简化了什么

我建议你亲手把两个版本都敲一遍,然后做一个对比。它们完成的功能完全一样,但代码量、可读性、心智负担有明显差异:

对比维度 原生 JS 版本 jQuery 版本
获取元素 document.getElementById / querySelector $() 一个函数搞定
创建元素 createElement 然后一个个 append 写 HTML 字符串即可
绑定事件 addEventListener 每个元素单独绑 .on() 支持事件委托
修改类名 classList.add / remove / toggle addClass / removeClass / toggleClass
插入元素 appendChild / removeChild append / remove
新元素事件 需要自己在委托逻辑里处理 .on() 天然支持委托

原生版不是不能写,而是同样的逻辑,jQuery 版更聚焦在"业务要做什么",而不是"浏览器 API 怎么调"。这正好体现了 jQuery 存在的意义:把底层差异和模板代码藏起来,让你专注业务逻辑。

5.5 扩展:用 localStorage 做数据持久化

上面这个案例刷新页面后数据就丢了。如果你想让任务保存下来,可以扩展一点,用 localStorage 存一个数组:

javascript复制// 读取本地存储
const savedList = JSON.parse(localStorage.getItem('todoList')) || [];

// 每次添加任务时
function saveTodo(text) {
  savedList.push(text);
  localStorage.setItem('todoList', JSON.stringify(savedList));
}

这个扩展足够你练习 JSON 序列化和反序列化。如果你想做得再完整一点,还可以在加载页面时把 savedList 里的数据渲染成列表;删除时从数组里剔除对应项再写回 localStorage。建议你在 jQuery 版本的基础上改,改造过程中自然就会碰到数组增删、对象序列化、DOM 重渲染这些高频知识点。

我这几年带新人的时候,发现一个规律:凡是先把原生 JS 基础打得很牢的人,学 jQuery 基本一天足够;凡是跳过原生直接上 jQuery 的,遇到报错就寸步难行。原因很简单,jQuery 再方便也是一层壳,壳下面的 DOM 模型、事件模型、作用域规则不会变。

所以这篇我在原生 JS 的地基部分花了不少篇幅,不是凑字数,而是希望你在动手敲 jQuery 的时候,心里清楚每一行 $() 到底在替你做什么。这比记住一百个 API 更有用。等你把这个待办事项案例来回改过几遍,JS 和 jQuery 的配合套路差不多就印在脑子里了。

内容推荐

LeetCode 1524题解:统计和为奇数的子数组数量
前缀和 · 模运算 · 子数组统计
前缀和是一种高效的数组预处理技术,能够快速计算任意子数组的和。其核心原理是通过存储累计和,将子数组求和问题转化为前缀和差值问题。在算法优化中,前缀和结合模运算可以显著降低时间复杂度,特别适用于统计特定条件的子数组数量这类问题。实际工程中,这种技术广泛应用于实时数据分析、金融交易监控等场景。以LeetCode 1524题为例,通过分析奇偶性的数学性质,将暴力解法的O(n²)复杂度优化至O(n),展示了算法思维在解决实际问题中的强大威力。掌握前缀和与模运算的组合应用,能够有效提升解决子数组相关问题的能力。
Redis替代方案Dragonfly:性能提升25倍的SpringBoot集成实战
Redis · Dragonfly · 内存数据库
内存数据库作为高性能数据存储的核心组件,其架构设计直接影响系统吞吐量和响应延迟。传统Redis采用单线程模型和经典哈希结构,在大数据量和高并发场景下容易出现性能瓶颈。新一代内存数据库Dragonfly通过创新的分层线程架构和SwissTable算法,实现了多核并行处理和内存效率优化,官方测试显示其吞吐量可达Redis的25倍。这种性能突破特别适合电商秒杀、实时推荐等需要处理突发高并发的场景。通过SpringBoot集成实践表明,Dragonfly在保持Redis协议兼容性的同时,能将平均响应时间从12ms降低到0.8ms,为Java开发者提供了一种高效的Redis替代方案。
C++多态机制:虚函数与抽象类的深度解析
C++多态 · 虚函数 · 抽象类
面向对象编程中的多态性是实现代码扩展性的核心技术,它通过动态绑定机制允许不同对象对同一消息作出不同响应。在C++中,虚函数和抽象类构成了多态实现的基础,其中虚函数表(vtable)的内存模型是实现运行时绑定的关键。这种机制在图形渲染、插件架构等场景具有重要价值,例如实现统一接口的图形绘制或数据库访问层。通过虚函数与纯虚函数的合理运用,开发者可以构建出既灵活又类型安全的系统架构,同时需要注意对象切片、虚函数默认参数等常见陷阱。现代C++还提供了override/final等关键字来增强多态代码的可维护性。
AI技术的能力与局限:从双生幻象到价值重构
人工智能 · AI技术 · Transformer
人工智能(AI)作为当前最前沿的技术领域之一,其核心在于通过算法和大数据模拟人类智能行为。从技术原理来看,现代AI主要依赖大规模预训练模型和海量数据训练,如Transformer架构和RLHF微调技术。然而,这种技术路线存在明显局限,包括数据依赖性、缺乏因果推理能力等,导致AI系统在实际应用中常出现表现与预期不符的'双生幻象'现象。在工程实践中,AI的价值困境尤为突出,例如在客服机器人和内容生成等场景中,虽然能提高效率,但仍需人工干预。要突破这一困境,需要结合多模态学习和神经符号系统等新技术,同时建立更全面的评估体系和人机协作机制。理解AI的能力边界与合理预期,是推动技术落地的关键。
知识图谱毕设选题指南:技术路线与创新策略
知识图谱 · 毕业设计 · 实体识别
知识图谱作为结构化知识表示的核心技术,通过实体、关系、属性三元组实现语义网络构建。其技术原理涉及信息抽取、表示学习与图数据库存储,在智能搜索、推荐系统等领域具有重要价值。随着Neo4j、BERT等开源工具的普及,知识图谱已成为高校毕设的热门方向,尤其在医疗健康、金融科技等场景展现强大应用潜力。针对毕业设计场景,推荐采用BiLSTM-CRF进行实体识别,结合Prompt-Tuning解决小样本关系抽取问题,并通过Echarts实现可视化演示。需特别注意数据标注规范与长尾分布处理,典型方案包括引入对比学习和类别加权损失函数。
LFP频域纯化技术:计算机视觉特征提取新突破
计算机视觉 · 特征提取 · LFP技术
在计算机视觉领域,特征提取是模型性能提升的关键环节。频域分析通过傅里叶变换将图像从空间域转换到频率域,能够更有效地分离有效特征与噪声。LFP(Low-frequency guided Frequency-domain Purification)技术创新性地结合频域分析与注意力机制,利用低频信号引导特征纯化,显著提升模型鲁棒性。该技术在ImageNet分类任务中实现Top-1准确率2.3%的提升,计算开销仅增加1.8%。其核心价值在于:通过频域维度重构特征表达,特别适用于无人机航拍、医疗影像等存在运动模糊或低光照噪声的场景。工程实践中,LFP模块可即插即用嵌入现有网络,配合动态高频阈值调整,在工业质检系统中使缺陷检测误报率降低52%。
出海产品社区冷启动的四大教训与实战策略
社区冷启动 · 出海产品 · 用户增长
社区冷启动是产品成功的关键阶段,尤其在出海场景下涉及复杂的跨文化因素。从技术实现角度看,冷启动本质上是用户增长模型与本地化算法的结合,需要解决的核心问题包括:文化适配的NLP处理、种子用户的质量评估体系、支付通道的技术整合等。在工程实践中,东南亚市场的数据显示,仅靠翻译UI的本地化方案留存率往往低于5%,而建立内容委员会机制后互动率可提升8倍。通过构建文化健康度监测系统和用户质量评估模型等技术手段,能有效规避早期常见的算法偏差问题。本文以社交产品出海为例,详解如何通过支付通道整合、合规预警机制等技术方案,实现月均UGC产出量提升27倍、自然裂变转化率超40%的实战效果。
Linux进程生命周期:从fork到exit的深度解析
Linux进程 · fork · 写时拷贝
进程管理是操作系统核心功能之一,Linux通过fork-exec-exit机制实现进程的完整生命周期。其中fork()系统调用采用写时拷贝(Copy-On-Write)技术,仅在需要修改时才复制内存页,大幅提升性能。进程终止时,内核通过do_exit()函数完成资源回收,包括释放内存、关闭文件描述符等关键操作。在多进程编程中,正确处理僵尸进程和信号终止是保证系统稳定性的关键,特别是在容器化环境中,PID 1进程需要妥善处理孤儿进程回收。理解这些底层机制,有助于开发者编写更高效、更可靠的并发程序,避免资源泄漏和进程管理相关的问题。
机器人工程师如何用几何思维掌握线性代数
线性代数几何理解 · 机器人SLAM · 自动驾驶感知
线性代数作为机器人感知与控制的数学基础,其几何本质在SLAM和自动驾驶领域具有关键作用。从空间变换的矩阵表示到传感器数据的特征分解,几何视角能帮助工程师直观理解旋转矩阵、特征向量等概念的实际物理意义。在点云配准中,奇异值分解(SVD)通过主轴对齐实现稳定求解;在位姿图优化中,李群理论保证了流形上的正确运算。通过结合Eigen、ROS等工具链的可视化实践,开发者能建立矩阵运算与刚体运动、相机投影等几何变换的直接关联,显著提升算法调试效率。特别是在自动驾驶的Occupancy Networks等应用中,几何直觉对理解高维特征映射至关重要。
Ubuntu系统安装PostgreSQL 17数据库指南
PostgreSQL · Ubuntu · 数据库安装
关系型数据库是数据存储和管理的核心技术,PostgreSQL作为功能最强大的开源关系数据库,以其标准兼容性和扩展性著称。其核心原理基于多版本并发控制(MVCC)机制,支持ACID事务特性,特别适合处理复杂查询和大规模数据。在Ubuntu等Linux系统上部署PostgreSQL能获得最佳性能表现,本文详细介绍在Ubuntu LTS版本上安装最新PostgreSQL 17的完整流程,包括系统配置、性能调优和安全设置等关键步骤,帮助开发者快速搭建高效稳定的数据库环境。
Spark ML在电商销售预测中的实战应用与优化
Spark ML · 销售预测 · 电商
机器学习在电商领域的应用日益广泛,其中销售预测是核心场景之一。通过分析历史销售数据、促销活动和外部因素,可以构建预测模型辅助决策。分布式计算框架Spark ML结合传统机器学习算法,能够有效处理海量结构化数据。本文以电商大促为背景,详细介绍了基于Spark ML的销售预测系统实现,包括特征工程、模型选择、超参数调优等关键技术环节。特别针对决策树和梯度提升树(GBT)算法在分布式环境下的优化实践进行深入探讨,并分享了生产环境部署中的性能调优技巧。该方案在千万级SKU预测场景中,将准确率提升至89%,显著优化了库存周转效率。
多频外差法在结构光三维测量中的原理与应用
结构光三维测量 · 多频外差法 · 相位解包裹
结构光三维测量作为非接触式光学测量技术,通过投影编码图案和相位解析实现物体三维重建。其核心技术相位解包裹直接影响测量精度,传统单频方法在复杂表面易产生误差。多频外差法通过投射特定频率比的光栅图案,构建等效波长解决相位跳变问题,显著提升测量稳定性。该技术结合DLP投影仪和全局快门CMOS相机等硬件,配合四步相移算法和三维坐标转换,广泛应用于工业检测和文物数字化等领域。特别是在处理不连续表面时,自适应频率选择和区域分割策略能有效保证测量精度。
4款AI论文辅助工具实测:提升学术写作效率
AI论文工具 · 学术写作 · Elicit
AI技术正在重塑学术写作流程,从文献检索到论文排版都出现了智能化解决方案。基于自然语言处理和机器学习的技术原理,这些工具能自动完成文献分析、结构优化和格式调整等重复性工作,显著提升研究效率。在科研场景中,Elicit等工具通过语义理解实现精准文献推荐,Scite.ai则提供智能引用支持,两者结合可优化文献综述环节。同时,Trinka和Overleaf分别解决了学术语言规范和排版格式的痛点。合理使用这些AI写作助手,既能保证学术诚信,又能将更多精力投入核心创新点的思考。
数组与循环边界处理的四大经典场景与避坑指南
数组边界 · 循环条件 · 动态规划
在算法与编程实践中,数组索引和循环边界处理是开发者常遇到的难点。计算机科学中数组通常采用0-based索引,这与人类自然的1-based计数习惯存在认知差异,导致边界条件容易出错。理解这一原理对编写健壮代码至关重要,特别是在动态规划、二分查找等场景中。动态规划需要额外考虑初始状态,因此dp数组常定义为n+1大小;二分查找的mid计算与边界更新则需要严格匹配循环条件。这些技术在LeetCode算法题、系统开发等场景广泛应用。通过掌握字符串处理的预留空间、循环终止条件等关键点,结合防御性编程技巧,可以有效避免80%的边界错误。本文总结的检查清单和实战案例,特别针对动态规划数组越界、二分查找死循环等高频问题提供了解决方案。
Embedding与Rerank技术解析:搜索推荐系统核心差异与实践
Embedding · Rerank · 搜索推荐系统
在搜索推荐系统中,Embedding技术通过将文本、图像等数据映射到低维向量空间,解决相似性匹配问题,而Rerank技术则对初步检索结果进行精细化排序。Embedding模型如Sentence-BERT和bge-large-zh-v1.5在通用文本检索和跨模态搜索中表现优异,而Rerank通过特征工程和模型推理提升排序精度。两者结合可显著提升搜索满意度,适用于电商、视频推荐等场景。
SpringBoot中获取真实客户端IP的解决方案与实践
SpringBoot · 客户端IP · X-Forwarded-For
在Web开发中,获取客户端真实IP是安全审计、访问控制等场景的基础需求。HTTP协议本身通过RemoteAddr字段记录连接来源,但在现代多层代理架构(如CDN→负载均衡→应用服务器)中,这个值会被逐层替换。行业通用解决方案是解析X-Forwarded-For请求头,其原理是代理服务器会按顺序追加经过的节点IP。需要注意的是,该头部存在被伪造风险,需结合信任代理IP列表进行安全校验。本文以SpringBoot为例,详细演示如何通过过滤器实现IP解析,并覆盖IPv6处理、日志脱敏等生产级实践,最后给出与Spring Security、Redis限流等组件的集成方案。
YOLOv8融合多尺度卷积注意力机制提升小目标检测
YOLOv8 · 多尺度卷积注意力机制 · 小目标检测
目标检测是计算机视觉的核心任务,其核心挑战在于多尺度目标识别。传统卷积神经网络通过固定感受野提取特征,难以兼顾不同尺寸目标。多尺度卷积注意力机制(MSCA)通过并行金字塔卷积核捕获局部到全局特征,结合通道注意力实现动态加权,显著提升小目标检测性能。该技术特别适用于工业质检、安防监控等场景,在YOLOv8框架中集成MSCA模块后,无人机航拍数据集上的小目标mAP提升11.6%,推理延迟仅增加3.2ms。工程实践中需注意模块插入位置选择、训练参数调整及部署优化技巧,如TensorRT加速和边缘设备适配。
ActiveMQ核心原理与SpringBoot集成实战
ActiveMQ · JMS · 消息队列
消息队列作为分布式系统解耦的关键组件,其核心原理是通过异步通信实现生产者和消费者的松耦合。JMS规范定义了Java平台访问消息中间件的标准接口,而ActiveMQ作为其开源实现,支持点对点(Queue)和发布订阅(Topic)两种消息模型。在SpringBoot项目中,通过starter-activemq依赖可快速集成,配合JmsTemplate简化消息收发操作。针对性能优化,ActiveMQ的Prefetch机制控制消息预取数量,合理设置可平衡吞吐量与内存消耗。典型应用场景包括订单处理、日志收集等需要异步解耦的业务场景,其中消息顺序保证、幂等处理等工程实践尤为重要。
Python命名空间与作用域:LEGB规则与最佳实践
Python · 命名空间 · 作用域
命名空间和作用域是Python编程中的基础概念,理解它们对编写高效、可维护的代码至关重要。命名空间是名称到对象的映射,而作用域决定了这些映射的可见范围。Python采用LEGB规则(Local→Enclosing→Global→Built-in)进行变量查找,这一机制直接影响代码的执行效率和可读性。在工程实践中,合理使用global和nonlocal关键字、优化局部变量访问、避免命名冲突等都是提升代码质量的关键技巧。特别是在闭包、装饰器和类继承等高级场景中,正确理解作用域链可以避免常见的变量覆盖和状态共享问题。通过遵循最小化全局变量、明确命名约定等最佳实践,开发者可以构建更健壮的Python应用程序。
Redis 7持久化机制详解与性能优化实践
Redis持久化 · RDB · AOF
Redis作为高性能内存数据库,其持久化机制是确保数据安全的关键技术。RDB通过生成数据快照实现高效备份,而AOF则记录所有写操作命令保证数据完整性。Redis 7引入混合持久化模式,结合RDB和AOF优势,大幅提升数据恢复效率。在电商、金融等高并发场景中,合理配置持久化策略能平衡性能与数据安全。通过调整save阈值、启用AOF重写等优化手段,可有效解决内存数据库常见的持久化性能瓶颈问题。本文基于Redis 7新特性,分享生产环境中RDB配置优化和AOF同步策略的最佳实践。
已经到底了哦
精选内容
热门内容
最新内容
Python命名空间与作用域深度解析及实战技巧
命名空间和作用域是编程语言中的基础概念,它们决定了变量的可见性和生命周期。在Python中,命名空间采用字典结构实现名字到对象的映射,而作用域遵循LEGB规则(Local→Enclosing→Global→Built-in)进行变量查找。理解这些机制对于编写高效、可维护的代码至关重要,特别是在处理闭包、装饰器和元编程等高级特性时。通过合理使用global和nonlocal关键字,可以精确控制变量作用域。在实际开发中,作用域管理直接影响代码性能和内存使用,例如在循环中缓存全局函数引用可提升30%以上的执行效率。本文结合快递柜和商场导航的生动类比,深入解析Python作用域机制,并给出多线程环境下的最佳实践方案。
YOLOv8改进:多尺度空洞注意力提升目标检测精度
目标检测是计算机视觉的核心任务,YOLO系列因其出色的实时性能成为工业界首选。针对复杂场景下的多尺度目标检测难题,多尺度空洞注意力(MSDA)机制通过融合不同扩张率的空洞卷积和优化注意力结构,显著提升模型特征感知能力。该技术在保持YOLOv8原有速度优势的同时,使小目标检测精度提升15%以上,特别适用于卫星图像分析和工业质检等高精度场景。实验证明,改进后的YOLOv8-MSDA在VisDrone数据集上mAP提升3.2%,且通过动态权重融合策略,比传统注意力模块推理速度提高17%。
Spark ML在零售销量预测中的实战应用与优化
机器学习在零售行业的销量预测中扮演着重要角色,通过分析历史销售数据,可以构建预测模型来优化库存管理和促销策略。分布式计算框架如Spark ML能够高效处理大规模数据,提升模型训练速度。本文以梯度提升树(GBT)算法为例,详细介绍了从数据清洗、特征工程到模型调优的全流程实践。特别探讨了如何利用移动平均、周期特征等时序特征提升预测准确率,以及通过AWS EMR集群实现分布式训练的技术方案。对于需要处理非线性销售数据关系的场景,GBT算法相比传统线性回归展现出明显优势。
网卡特性优化:提升网络吞吐性能的关键技术
网络吞吐性能优化是现代数据中心和云计算环境中的核心挑战,其中网卡硬件卸载技术发挥着关键作用。通过TSO(TCP Segmentation Offload)、GRO(Generic Receive Offload)等特性,可以将原本由CPU处理的网络包分片/重组工作转移到网卡硬件执行,显著降低CPU负载并提升吞吐量。这类硬件加速技术特别适合视频流媒体、金融交易等高吞吐场景,实测显示合理配置可带来18-25%的性能提升。但在IoT设备通信等小包场景中,反而需要关闭Offload特性以避免性能下降。工程师需要结合RSS多队列技术和中断亲和性设置,根据具体业务特征(如包大小、流量模式)进行针对性调优,并通过分阶段测试验证配置效果。
MCP协议:AI工具互联互通的核心技术与实践
在分布式系统与AI工程领域,服务间通信协议是解决异构系统互操作性的关键技术。MCP协议通过分层架构设计,将传输层、消息格式层与语义层解耦,实现了类似网络协议栈的灵活通信能力。其核心价值在于标准化AI工具间的交互方式,通过定义任务请求、结果返回等基础消息类型,配合JSON Schema验证机制,确保跨语言、跨平台服务的可靠通信。典型应用场景包括AI工具链集成、微服务编排等,其中服务发现机制与负载均衡策略能显著提升系统扩展性。该协议特别适合处理图像识别、自然语言处理等AI组件的协同工作,实测显示可降低70%的跨服务延迟。随着流式处理与边缘计算等新特性的加入,MCP正在成为AI工程化领域的重要基础设施。
Ubuntu系统下PostgreSQL 17安装与优化全指南
PostgreSQL作为开源关系型数据库的代表,以其强大的功能和稳定性广受开发者青睐。其核心架构采用多进程模型,通过WAL日志机制确保数据持久性,并支持ACID事务特性。在性能优化方面,PostgreSQL 17版本显著提升了并行查询效率,结合合理的内存参数配置(如shared_buffers)可大幅提升吞吐量。特别是在Ubuntu LTS环境(22.04/24.04)中部署时,需要注意内核参数调优(如vm.swappiness)和透明大页(THP)禁用等系统级优化。实际应用场景涵盖从Web应用到地理信息系统,配合PostGIS等扩展可实现空间数据高效处理。本文详细记录了在Ubuntu各版本上部署PostgreSQL 17的全流程,包含软件源配置、安全加固、性能调优等实战经验,特别针对pg_hba.conf认证配置和WAL日志归档等关键环节提供了最佳实践方案。
AI安全新挑战:语料投毒攻击与防御实战
在机器学习与人工智能领域,数据质量直接影响模型性能。语料投毒作为一种新型对抗攻击手段,通过污染训练数据诱导AI产生错误输出,其危害性在生成式AI时代被显著放大。从技术原理看,攻击者主要利用语义劫持、事实污染和逻辑腐蚀三种路径,这些手法能绕过传统检测方法。防御体系需从数据清洗、运行时监控等多维度构建,其中溯源验证、对抗样本检测和知识图谱校验是关键环节。当前在金融风控、医疗诊断等高危领域,建立动态权重隔离和输出溯源机制已被证明能有效降低风险。随着多模态模型普及,防范语料投毒已成为保障AI系统可靠性的重要课题。
电力系统多模态数据融合与事理图谱故障诊断实践
多模态数据融合是工业物联网中的关键技术,通过整合电气量、振动、红外等多源传感器数据,结合动态时间规整(DTW)等算法实现μs级时间对齐。事理图谱作为因果推理的核心工具,采用改进的Pearson-Granger-TransferEntropy混合权重计算,能有效捕捉设备间隐性关联。在电力系统故障诊断场景中,神经符号AI融合架构(CNN-LSTM+GAT+Prolog)实现了92.3%的准确率,特别适用于主变绕组短路、电容器熔丝熔断等复杂故障定位。典型部署方案采用Xilinx Zynq UltraScale+与NVIDIA Jetson AGX Orin的异构计算架构,通过边缘计算节点实现实时诊断,现场应用表明其使新型故障适应速度提升60%。
Java包装类不可变性原理与实践解析
在Java编程中,不可变对象(Immutability)是保证线程安全和数据一致性的重要设计模式。其核心原理是通过final修饰符、私有化字段和禁用修改方法,确保对象状态在创建后不可改变。这种特性天然适用于多线程环境,无需同步即可实现线程安全,同时保障了哈希集合操作的可靠性。以Integer为代表的包装类通过享元模式缓存常用值,在-128到127范围内复用对象以优化内存。实际开发中需注意自动装箱的性能损耗,大数运算场景建议优先使用基本类型或AtomicLong等替代方案。理解不可变对象的实现机制,有助于设计更健壮的Value Object和并发安全系统。
Cell bin应用场景与选购指南:从制造业到物流的智能容器
Cell bin作为工业存储单元的核心组件,其设计原理融合了材料科学与人体工学。通过HDPE、PP等工程塑料的材质演进,现代cell bin已具备耐温、抗冲击等物理特性,而三层复合结构更将使用寿命提升至8年。在技术价值层面,集成RFID、重量传感器等物联网模块的智能cell bin,实现了从被动容器到主动管理节点的转变,特别在药品冷链等场景中,能将异常监测响应时间缩短94%。应用场景覆盖汽车制造(需承重50kg)、跨境电商物流(支持动态容积调节)和生物实验室(要求医用级PMMA材质)三大领域,其中制造业标准化与物流柔性化需求形成鲜明对比。合理的选购需关注堆叠承载800kg等力学指标,而抗静电10^6-10^9Ω等特殊参数则决定科研场景的使用安全。
已经到底了哦