ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JavaScript核心内容拆解:从基础语法到事件循环与DOM实战

JavaScript核心内容拆解:从基础语法到事件循环与DOM实战 很多人第一次接触 JavaScript都会被一个问题卡住到底要学哪些内容才算“会 JS”光是网上搜“js主要内容”“js教程”之类的词出来的目录清单都能把人吓退。今天这篇不搞百科全书的堆砌我按自己这些年做前端的实际经验把 JavaScript 的核心内容拆成一张学习地图。从变量这种地基到事件循环、DOM 实战这些真正干活的东西一路梳理到进阶方向你看完就知道该往哪儿使劲。1. JavaScript 这门语言到底在学什么先搞清楚大前提JavaScript 目前是 Web 前端的唯一编程语言HTML 负责页面结构、CSS 负责样式而 JS 负责“行为”。你在页面上看到的所有交互——点击弹窗、轮播图、表单校验、动态加载数据、页面局部刷新、拖拽上传背后基本都是 JS 在工作。但 JS 的能力范围早就超出了浏览器。Node.js 让它能在服务端跑Electron 拿它做桌面应用uni-app、React Native 拿它做移动端小程序也用它写逻辑。所以“js主要内容”这个词涵盖的面特别宽。我的建议是如果你是个刚入门的新手先别被这些分支绕晕把语言本身的核心语法吃透这部分在哪个环境都一样。我曾见过不少人一上来就扎进框架里最后连闭包、原型链都讲不清楚一到面试或者排查 bug 就露怯。按我理解JS 的学习主体可以分成五大块基础语法、函数与作用域、对象与数组、异步编程、DOM 操作。前四块是语言自身的底子最后一块是浏览器端实战的入口。这五大块能覆盖日常开发 80% 以上的需求。像热搜里出现的“js event loop 过程”“js 执行上下文与变量提升”“js promise”都属于异步和机制的范畴是面试必问、也是实际写代码最容易踩坑的地方。还有一堆热搜词比如“js 数组”“js 函数”“js 数据类型”对应基础语法“js 三级联动”“js 元素拖拽事件”“js p 标签 垂直居中”则属于 DOM 操作和具体业务场景。这些其实都是上面五大块拆分出来的子问题。把主体框架搭好再逐个击破细节才是最高效的路线。接下来的内容我就照这个主线的顺序往下拆。2. 地基变量、数据类型与运算符先看变量声明。ES6 之后推荐用let和const而不是var。三者的核心区别在于作用域和变量提升行为。var是函数级作用域let和const是块级作用域var存在变量提升声明之前访问不报错但值是undefined而let和const在声明前访问会直接抛出暂时性死区错误。日常开发中默认const确实需要重新赋值才用letvar我基本不再碰。// var 的变量提升 console.log(a); // undefined var a 10; // let 的暂时性死区 console.log(b); // ReferenceError: Cannot access b before initialization let b 20;数据类型方面JS 有七种内置类型string、number、boolean、undefined、null、symbol、bigint加上object。其中前七种是原始类型object是引用类型。这里有个高频考点typeof null返回的是object这是个历史遗留 bug但几乎每套面试题里都有。判断数据类型的方式有几种typeof适合判断原始类型instanceof适合判断对象的具体类型Object.prototype.toString.call()是最稳妥的通用方案。比如要判断一个值是不是数组用Array.isArray()最靠谱用typeof判断数组得到的是object会踩坑。JS 的运算符要特别注意隐式类型转换。比如1 2得到字符串12而1 - 2得到数字-1。加号遇到字符串就拼接减号会尝试把两边转成数字。这类问题在写表单校验、处理接口返回数据时特别常见。我在实际开发里养成一个习惯凡是涉及数字计算的场景明确用Number()或parseInt()做转换绝不依赖隐式转换。还有个实用的技巧判断一个值是否为合法数字用Number.isNaN()而不是全局的isNaN()因为全局isNaN会把字符串abc也算成 NaN。模版字符串也是日常用得极多的语法用反引号包裹并支持${}插值替代掉字符串拼接后代码可读性提升一大截。解构赋值的出现让变量取值变成一件很爽的事。const user { name: 张三, age: 25 }; const { name, age } user; const arr [1, 2, 3]; const [first, second] arr;3. 分支、循环与函数写代码的基本骨架任何程序都离不开顺序、分支、循环三种结构。JS 的分支主要用if...else、switch、三元运算符。if...else适合范围判断switch适合固定值的多分支匹配三元运算符则适合简单的二选一赋值。写代码时我提倡“早返回”策略把不符合条件的情况提前拦截掉减少嵌套层级代码更清爽。function getStatusText(status) { if (status 1) return 待审核; if (status 2) return 审核通过; if (status 3) return 审核驳回; return 未知状态; }循环方面for循环适合知道循环次数的场景while适合不确定次数、按条件退出的场景。ES6 之后for...of可以直接遍历数组和可迭代对象比传统的for循环简洁得多。但要注意for...in和for...of的区别前者遍历的是键名适合对象属性遍历后者遍历的是键值适合数组和类数组。const arr [10, 20, 30]; for (const item of arr) { console.log(item); // 10 20 30 } const obj { a: 1, b: 2 }; for (const key in obj) { console.log(key, obj[key]); // a 1 / b 2 }函数是 JS 里最核心的概念它不只是“代码块”更是一等公民。函数可以赋值给变量、作为参数传递、作为返回值返回。这也是“函数式编程”在 JS 里能够流行的基础。定义函数的方式有函数声明、函数表达式、箭头函数三类。箭头函数除了写法简洁最大的特点是不绑定自己的this它会捕获定义时上下文里的this。这个特性在处理事件回调、定时器时特别有用能避免传统写法里this指向错乱的坑。// 传统 function 的 this 指向调用者 const btn document.getElementById(btn); btn.addEventListener(click, function () { console.log(this); // btn 元素 }); // 箭头函数的 this 指向定义位置 btn.addEventListener(click, () { console.log(this); // window取决于定义位置 });函数参数方面默认参数、剩余参数、展开运算符都是高频用法。默认参数让函数更健壮不用在函数体里手动判空剩余参数...args可以把不确定个数的参数收集成数组展开运算符则可以把数组或对象展开到另一个结构中做浅拷贝非常方便。关于作用域JS 是词法作用域也就是变量的可访问范围由代码书写的位置决定而不是调用位置。这个特性引出了一个概念闭包。简单理解闭包就是“函数记住了它出生时的作用域”。这个能力让函数可以访问外部函数的变量即使外部函数已经执行结束。闭包在封装私有变量、防抖节流、柯里化等场景中都有大量应用。function createCounter() { let count 0; return function () { count 1; return count; }; } const counter createCounter(); console.log(counter()); // 1 console.log(counter()); // 24. 对象与数组组织数据的两大核心对象是 JS 里组织和传递数据的基本单元。在实际开发中接口返回的数据、页面的状态、组件的配置全都是对象。对象的基本操作无非是增、删、改、查但这些操作里藏着很多细节。读取属性有两种方式点语法obj.name和方括号语法obj[name]。当属性名是变量、或者包含特殊字符时必须用方括号语法。删除属性用delete关键字但值得注意delete只能删除对象自身的属性不能删除原型链上的属性。遍历对象的方法有for...in、Object.keys()、Object.values()、Object.entries()。Object.keys()最常用它返回对象所有可枚举属性的名称数组。ES6 的对象方法简写、计算属性名、解构赋值、展开运算符也极大提升了开发效率。比如合并两个对象用展开运算符一行搞定比Object.assign()更直观。const defaults { theme: light, pageSize: 20 }; const userConfig { pageSize: 50 }; const finalConfig { ...defaults, ...userConfig }; // { theme: light, pageSize: 50 }数组更是前端开发里绕不开的数据结构。热搜里的“js 数组”“js 数组方法”都说明了它的重要性。除了一般的增删改查数组的迭代方法才是真正的大头。map用于映射生成新数组filter用于筛选符合条件的元素reduce用于把整个数组合并成一个值forEach用于单纯的遍历。这几个方法能覆盖 90% 的数据处理场景。const numbers [1, 2, 3, 4, 5]; // map每个元素翻倍 const doubled numbers.map(n n * 2); // filter筛选偶数 const evens numbers.filter(n n % 2 0); // reduce求和 const sum numbers.reduce((acc, cur) acc cur, 0);这里我多说一句reduce的第二个参数坑。当不传初始值时reduce会把数组第一个元素当作初始值然后从第二个元素开始迭代。如果数组为空直接调用reduce会抛TypeError。稳妥的做法是永远给reduce传初始值除非你非常确定数组至少有一个元素。常见的业务场景——热搜里提到的“js三级联动”省市区联动和“学生管理系统实现学生信息的增删改查”——本质上都是对对象和数组的熟练操作。省市区三级联动核心就是根据选中的省去过滤市列表再根据选中的市去过滤区列表过滤逻辑用filter实现学生管理系统的增删改查核心就是数组的push新增、findIndex和splice编辑删除、以及条件过滤查询。把这些基础方法练熟了这类业务分钟就能写出来。5. 执行机制作用域链、this 与事件循环很多初学者在基础语法阶段学得很快一到“js 执行上下文与变量提升”“js event loop 过程”这里就卡住了。这部分确实抽象但恰恰是 JS 进阶的分水岭。我的建议是先把它当成“规则”去记忆再在实战中去验证。执行上下文可以理解为代码执行时的环境信息集合包含变量对象、作用域链、this指向。每个函数调用时都会创建自己的执行上下文JavaScript 引擎用执行上下文栈来管理它们。先进后出栈顶是当前正在执行的上下文。全局代码本身也是一个执行上下文它是这个栈的栈底。变量提升的现象根源在于创建执行上下文时JS 引擎会把变量和函数声明全部“提升”到作用域顶部。所以函数声明可以在定义之前调用而var声明的变量在赋值之前是undefined。let和const不会有这个问题是因为它们在提升阶段没有初始化形成了暂时性死区。this的指向是另一个高频难点。记住几个规则基本够用全局环境下的this指向全局对象浏览器里是window普通函数调用时this指向调用者对象方法里的this指向对象本身箭头函数没有自己的this沿作用域链向上找call、apply、bind可以显式指定this我做开发时最常用的排查方法是不确定this指向哪里就先console.log(this)看一眼或者使用call显式指定上下文避免依赖隐式绑定。还有一招在 React 类组件里事件处理函数如果不绑定this调用时拿到的就是undefined这就是“严格模式下this为 undefined”的一个典型案例。异步编程更是 JS 的重头。JS 是单线程语言但通过事件循环机制实现了异步不阻塞。事件循环的基本规则是执行同步代码遇到异步任务挂起主线程空闲后按顺序执行微任务队列再执行一个宏任务循环往复。宏任务包括整个脚本、setTimeout、setInterval、I/O 操作、UI 渲染微任务包括Promise.then、MutationObserver、queueMicrotask。理解这两者的顺序是回答“js event loop 过程”这类问题的关键。setTimeout(() console.log(宏任务1), 0); Promise.resolve().then(() console.log(微任务1)); Promise.resolve().then(() console.log(微任务2)); console.log(同步代码); // 输出顺序同步代码 - 微任务1 - 微任务2 - 宏任务1Promise是异步编程的主要工具它有三种状态pending、fulfilled、rejected状态一旦改变就不可逆。Promise链非常依赖then的返回值只要返回一个值或者新的Promise后面的then就能继续拿到数据。处理多个并发异步请求可以用Promise.all只需要其中一个成功就用Promise.race。到了 ES2017async/await的引入让异步代码写起来像同步代码可读性大幅提升。但注意用await时必须在外层的async函数里await后的异常需要用try...catch捕获或者让上层Promise链去处理。事件循环除了面试会考实际调试中更是救命技能。我曾经遇到一个场景页面上有个数据加载动画怎么都不消失。排查后发现加载动画的隐藏逻辑放在了setTimeout里而数据更新用的是Promise微任务先执行完了宏任务里的动画隐藏代码还在排队。把隐藏逻辑挪到微任务或者使用requestAnimationFrame问题立刻解决。这就是事件循环影响业务的真实例子。6. DOM 与浏览器实战从“会语法”到“能做页面”学完基础语法和异步机制后就要进入浏览器端的“实战环节”。DOM文档对象模型是浏览器把 HTML 解析成的一棵节点树JS 通过操作这棵树的节点实现页面的动态修改。热搜里的“js p 标签垂直居中”“js 元素拖拽事件”“js 实现复制粘贴”“js 验证 url 有效性”“iframe 关闭 jquery 并刷新父页面 js”都属于这一类。这里挑几个有代表性的场景透讲一下。获取 DOM 元素最常用的方式有document.getElementById、document.querySelector、document.querySelectorAll。querySelector的好处是可以用 CSS 选择器语法一次能把 ID、类名、属性选择器都覆盖到。注意querySelector只返回第一个匹配元素querySelectorAll返回 NodeList需要通过Array.from或者forEach去遍历。修改元素内容用textContent或者innerHTML前者只改文本、性能高且安全后者可以解析 HTML 标签、但容易引起 XSS 风险用户输入内容做展示时尽量别用innerHTML。事件绑定是 DOM 操作的重头戏。addEventListener是标准方式好处是同一个元素可以绑定多个事件处理器也支持移除监听。移除的关键是第三个参数是否捕获要和添加时一致并且回调函数不能是匿名函数否则无法移除。事件对象event里常用的是target实际触发事件的元素、currentTarget绑定事件的元素、preventDefault阻止默认行为、stopPropagation阻止冒泡。在事件委托里我们利用事件冒泡让父容器统一处理子元素的事件。这对动态插入的元素尤其友好不用每新增一个子元素就绑定一次事件。垂直居中是个经典问题。给一个p标签做垂直居中如果是单行文本给容器设置等高的line-height立即解决如果是多行文本或者任意元素现代浏览器推荐用 Flexbox.parent { display: flex; align-items: center; justify-content: center; }还有一种场景是一个块级元素在未知高度的容器里居中用绝对定位加transform: translate(-50%, -50%)最稳妥。说到底是盒模型和定位问题搞清楚父容器的定位上下文比死记硬背代码更重要。拖拽事件是可视化页面、低代码平台里的高频需求。HTML5 的拖放 API 提供了一整套事件dragstart、dragover、drop、dragend等。要实现一个拖拽排序基本步骤是给可拖拽元素绑定dragstart保存当前元素的信息给目标容器绑定dragover阻止默认行为这步不阻止后面的drop不会触发绑定drop通过判断拖拽位置动态调整顺序。不过 HTML5 拖放 API 在移动端表现一般如果需要做触屏拖拽通常要改用touchstart、touchmove、touchend事件组配合document.elementFromPoint来判断目标位置。复制粘贴这块现在有navigator.clipboardAPI可以一行完成复制await navigator.clipboard.writeText(要复制的内容);但注意这个 API 要求在安全上下文HTTPS下才能使用并且在调用时需要页面处于焦点状态。要兼容老浏览器备选方案是用一个隐藏的textareadocument.execCommand(copy)。我自己的经验是写一个工具函数先尝试clipboardAPI失败就回退到execCommand方案。验证 URL 有效性不要试图用正则把一个完整的 URL 解析明白那是自找麻烦。直接用URL构造函数去判断能new URL(url)不报错就是合法 URL还顺便能取协议、域名、路径等各部分function isValidUrl(url) { try { new URL(url); return true; } catch { return false; } }iframe 父页面通信也算一个经典痛点。要在 iframe 里面操作父页面的 DOM前提是同源。同源的话直接window.parent.document就可以访问父页面的文档。要把 iframe 关闭同时刷新父页面逻辑是// iframe 子页面内 window.parent.document.getElementById(iframeId).remove(); window.parent.location.reload();如果是用 jQuery 操作写法是$(#iframeId, window.parent.document).remove()。跨域场景不能用这种直接访问的方式需要走postMessage通信这里不展开但你心里要有个概念跨域时目标是window.parent.postMessage(data, origin)父页面用window.addEventListener(message, handler)接收。7. 进阶方向框架、工程化、可视化与专项能力把基础语法和 DOM 实战过完你就已经能做大部分静态交互页面了。但如果要往中高级前端走还有几个主要方向需要拓展。热搜里出现的东西可以作为线索vue、arcgis、js 逆向、反爬、ofd 电子发票解析、小程序等等其实都可以归类到这些方向里。第一个方向是前端框架。vue、react 是目前最主流的选择。框架解决的核心问题是“数据和视图的同步”。用原生 JS 操作 DOM 时数据一变就要手动去改页面页面一交互又要手动改数据非常繁琐。框架引入响应式系统让你只管数据视图自动更新。热搜里的“js深入浅出vue”说的就是这条线。学框架之前先确认自己的原生 JS 功底够用因为框架文档里到处是闭包、this、异步更新、依赖收集这类概念基础不牢会学得很痛苦。第二个方向是工程化。现在的大项目不可能用一堆 script 标签引入 JS而是用模块化、打包工具来处理。ES Module 是目前的标准化模块方案import/export语句来组织代码。webpack、vite 这类打包工具负责把成千上万个模块合并成浏览器能直接运行的产物顺带处理兼容性、压缩、静态资源等。实际项目里还有 TypeScript 的加入它给 JS 增加了静态类型检查开发大型项目时能减少大量低级错误。第三个方向是可视化。这个方向很有意思把数据变成图表和图形。ECharts 是最常用的图表库封装了折线图、柱状图、地图等几十种图表。WebGL 和 Three.js 则用于三维场景像智慧城市可视化大屏、数字孪生、3D 商品展示这类项目都有大量需求。可视化对“渲染性能”的理解要求比较高因为一屏几千个节点时如何避免重排重绘就是核心挑战。第四个方向是地图 GIS。热搜里的“arcgis api for js 部署”属于这个方向。ArcGIS API for JavaScript 很强大但部署起来需要配置静态资源路径。国内更常用的是 Leaflet、OpenLayers、高德地图 JS API 这类方案。核心能力是图层的加载、标注、空间查询、轨迹回放等。空间数据和普通数据最不一样的地方在于坐标系和投影搞懂经纬度和 Web 墨卡托的差异是入门第一课。第五个方向是前端安全与逆向。这个方向的现实需求来自爬虫对抗、加密参数分析、反爬策略等。热搜里的“js逆向”“js反爬实战”“ctfhub 文件上传 - js前端验证”都是这一类。学这个方向时要特别注意用途边界。从技术本身讲前端代码是公开的任何人都能看到源码了解代码执行逻辑、寻找加解密入口是安全测试和漏洞排查的常用手段。比如 CTFHub 里的文件上传题本质是让你识别出前端有一个纯 JS 的格式校验绕过校验直接构造上传请求这个训练能帮你理解一个道理前端校验只能改善体验不能作为安全边界。这类知识对做好自己的前端防护非常有价值。但我不建议把精力花在破解商业软件、绕过付费机制上那既没有技术成长又有法律风险。还有一个方向是跨端开发。小程序、uni-app、Taro、React Native、Electron 都是 JS 生态向其他平台延伸出来的。学一个跨端框架的核心收益是“一次编写、多处运行”但代价是平台特性和底层 API 的差异会时不时冒出来。热搜里的“renderjs 如何引用 js”和 uni-app 有关renderjs 在 uni-app 里是用来运行复杂逻辑或直接操作 DOM 的特殊模块本质还是 JS 在 WebView 环境中的运行机制问题。至于“js 宏”属于 Microsoft Office 和 WPS 里通过脚本实现办公自动化的扩展能力和页面前端不是一条线但思路相通用脚本控制宿主应用。另外“js 散度”属于信息论和概率统计的概念在前端只用得上很浅的部分不需要专门深挖。“js 如何读取 ofd 电子发票信息”属于电子政务场景核心是理解 OFD 的包结构、XML 内容组织再用 JS 做解析渲染这类项目靠的是逆向文件格式的钻研劲而不是什么神秘的库。8. 一条可执行的学习路线与我的个人建议最后给你一条经验味很重的学习路径这是带过不少新人得出的实际结论。纯看书做题和真正的“会写 JS”之间隔着一道巨大的鸿沟跨过去的方法只有一个多在真实页面上动手。建议把大目标拆成三个阶段。第一阶段只写控制台程序练习题可以是数组去重、字符串反转、统计字符出现次数、用递归实现扁平数据转树形结构、手写防抖节流。这些题目不碰 DOM纯粹练语法和逻辑。第二阶段做网页小项目比如省市区三级联动、学生信息管理、一个自用的待办清单、一个记事本应用这些能把 DOM 操作、事件绑定、数组方法全部串起来。第三阶段再碰框架和工程化用 Vue 或者 React 重构第二阶段做过的小项目你会深刻理解框架到底优化了什么。热搜词里出现了一堆“javascript 学习手册”说明很多人想靠系统性的文档入门。文档肯定要读但不建议从第一页读到最后一页。我的建议是把文档当成字典带着问题去翻。比如你写列表渲染时发现改了数组数据页面没更新就去查“Vue 数组更新检测”你在处理异步顺序时发现代码不按预期执行就去查“事件循环”。带着业务痛点的学习效率比漫无目的的翻阅高得多而且记忆点极深。调试能力必须跟上。别一遇到问题就console.log到处打印。浏览器 DevTools 的 Sources 面板可以直接打断点逐步执行还能看作用域链和调用栈Network 面板能看每个请求的参数、响应、耗时Performance 面板能定位页面卡顿的瓶颈。学会用断点调试和网络面板相当于给自己开了一双天眼。我见过太多人花了半天在代码里加日志结果发现是后端接口报错——浏览器网络面板几分钟就能定位到。还有一个细节容易被忽略代码规范。变量命名不是小事。data、temp、aaa这种名字在写的时候很爽三天后回来看代码会一头雾水。我习惯用userList表示用户数组、用isLoading表示加载状态、用handleClick表示事件处理函数。虽然 JS 对命名没要求项目里如果安装了 ESLint最好从一开始就跟着它的规范走好习惯能省掉后面无数坑。写到这里JavaScript 的核心面貌应该已经出来了。它的内容看着特别多但本质就是一条线先会变量、数据类型、函数这些最小砖块再用对象和数组搭出数据结构配合事件循环管好异步然后拿 DOM 去操作页面最后靠框架和工程化去做大项目。每一步都有明确的目的不必焦虑自己要懂所有的新词先把最常用的东西练熟你会发现自己其实比想象中走得快。
返回列表