
不少刚接触前端的同学都有过类似的困惑看了很多视频课语法都认识但一到自己写页面、调 bug、刷面试题的时候就发懵。JavaScript 作为前端的核心语言它的知识密度其实不低如果只是零散地背 API很容易陷入“学了就忘、忘了再学”的循环。这篇文章会把 JavaScript 的基础知识系统串一遍从变量、类型、函数、对象、异步到 DOM 操作再到一个完整的实战案例尽量做到“全程无废话”。内容适合正在准备前端入门、想巩固 JS 基础的开发者也适合在面试前快速过一遍核心考点的同学。1. 理解 JavaScript它到底是什么1.1 JavaScript 不是 Java很多人第一次听到 JavaScript 这个名字会以为它和 Java 有什么关系。实际上两者除了语法上部分相似之外几乎是两个完全不同的语言。Java 是静态类型的编译型语言常用来做后端服务、Android 应用JavaScript 是动态类型的解释型语言主要运行在浏览器中负责网页的动态交互。随着 Node.js 的出现JavaScript 也可以运行在服务器端所以它并不只是“页面小脚本”而是一门全栈语言。哪怕你以后用 Vue、React 这类框架底层依然是 JavaScript只是框架帮你封装了 DOM 操作和状态管理。1.2 JavaScript 能做什么在前端领域JavaScript 的职责可以简单概括为三点操作页面结构DOM例如新增一个列表项、删除某条数据、修改元素的样式。响应用户操作事件例如点击按钮、输入表单、滚动页面。与服务器通信网络请求例如提交表单数据、拉取列表数据。在实际开发中JavaScript 还会参与数据校验、动画控制、状态管理、路由控制等任务。可以说没有 JavaScript 的网页只能算一张静态海报。1.3 JavaScript 的运行环境JavaScript 需要运行环境才能执行。两种最常见的环境环境说明使用场景浏览器内置 JavaScript 引擎前端页面交互Node.js基于 V8 引擎的服务端运行时后端开发、前端工程化工具本文的示例以浏览器环境为主你只需要打开浏览器控制台F12切到 Console 面板就可以直接运行后面的代码。2. 环境准备浏览器控制台就够了学习基础语法并不需要安装复杂的 IDE。建议直接使用 Chrome 或 Edge 浏览器按 F12 打开开发者工具切到 Console 标签页就能输入并运行 JavaScript 代码。如果需要写多个文件的完整页面建议使用 VS Code并按照下面的目录结构创建项目js-quickstart/ ├── index.html ├── style.css └── main.js本文的实践案例也会按照这个结构来组织。如果你还没有 VS Code安装最新稳定版即可如果不是专业前端开发不必纠结编辑器配置先用默认设置跟着写。3. JavaScript 核心语法精讲3.1 变量声明var、let、const 怎么选变量是编程语言的基本单位。ES6 之前使用var声明变量ES6 之后推荐使用let和const。三者区别如下声明方式作用域变量提升可否重复声明可否重新赋值var函数作用域是可以可以let块级作用域否有暂存死区不可以可以const块级作用域否有暂存死区不可以不可以看几个简单示例// var 存在变量提升 console.log(a); // undefined而不是报错 var a 1; // let 在声明前访问会报错 console.log(b); // ReferenceError: Cannot access b before initialization let b 2; // const 声明时必须初始化 const c; // SyntaxError: Missing initializer in const declaration日常开发中推荐遵循一个原则默认优先使用const只有当变量的值确实需要重新赋值时才使用let尽量不要使用var。这样可以在编码阶段减少很多意料之外的赋值问题。3.2 数据类型原始类型与引用类型JavaScript 的数据类型可以分为两大类原始类型number、string、boolean、null、undefined、symbol、bigint引用类型object包含数组、函数、日期等它们的核心区别在于存储方式// 原始类型的赋值是值拷贝 let a 10; let b a; a 20; console.log(b); // 10b 不受 a 影响 // 引用类型的赋值是引用传递 let arr1 [1, 2, 3]; let arr2 arr1; arr1.push(4); console.log(arr2); // [1, 2, 3, 4]arr2 跟着变了理解了这一点后面遇到“修改一个对象导致另一个对象也变化”的问题就不会一脸茫然了。3.3 类型转换的常见坑位JavaScript 是弱类型语言在比较和运算时会自动做类型转换。这也是很多新手容易踩坑的地方console.log(1 1); // 11数字连接字符串 console.log(1 - 1); // 0字符串被转成数字 console.log(0 false); // true因为 false 被转成 0 console.log(0 false); // false严格比较不会做类型转换建议开发中尽量使用严格相等避免隐式转换带来的奇怪问题。如果要判断一个值是否为null或undefined更推荐使用 null这种写法。3.4 流程控制与循环流程控制和其他语言差别不大const score 85; if (score 90) { console.log(优秀); } else if (score 60) { console.log(及格); } else { console.log(不及格); } for (let i 0; i 5; i) { console.log(i); // 依次输出 0 1 2 3 4 } const arr [a, b, c]; for (const item of arr) { console.log(item); }for...of适合遍历数组和可迭代对象for...in更适合遍历对象属性但要注意它会把原型链上的属性也遍历出来实际开发中建议配合hasOwnProperty使用或者直接使用Object.keys、Object.entries。4. 函数与作用域前端面试的核心区4.1 函数声明与函数表达式函数是 JavaScript 的一等公民可以像普通变量一样被传递和返回。// 函数声明 function add(a, b) { return a b; } // 函数表达式 const addFn function (a, b) { return a b; }; // 箭头函数 const addArrow (a, b) a b;函数声明存在提升可以在声明之前调用函数表达式和箭头函数则不行。4.2 this 指向问题this是 JavaScript 中容易混淆的点但又是面试高频考点。普通函数中this指向调用它的对象。箭头函数没有自己的this它继承定义时的外层作用域this。使用call、apply、bind可以手动指定this。看一个对比const person { name: 张三, greetNormal: function () { console.log(普通人, this.name); }, greetArrow: () { console.log(箭头函数, this.name); } }; person.greetNormal(); // 普通人张三 person.greetArrow(); // 箭头函数undefined外层是 window箭头函数在定义时就固定了this所以不能作为构造函数使用也不适合在需要动态this的场景下使用。4.3 闭包函数记住了外部变量闭包指的是一个函数“记住”并访问了它所在作用域的变量即使外部函数已经执行完毕。function createCounter() { let count 0; return function () { count; return count; }; } const counter createCounter(); console.log(counter()); // 1 console.log(counter()); // 2闭包的经典应用场景包括封装私有变量、防抖节流、函数柯里化、React Hook 的实现原理等。理解闭包对阅读框架源码也有很大帮助。5. 数组与字符串工作中最常用的两类数据5.1 字符串常用方法字符串是前端数据处理中最常见的基础数据类型。以下方法需要熟练掌握const str Hello, JavaScript; str.length; // 17 str.toUpperCase(); // HELLO, JAVASCRIPT str.includes(Java); // true str.startsWith(He); // true str.endsWith(pt); // true str.slice(0, 5); // Hello str.split(, ); // [Hello, JavaScript] str.trim(); // 去除首尾空格 str.replace(JavaScript, JS); // Hello, JS注意字符串是原始类型所有返回新字符串的方法都不会修改原字符串。如果需要拼接大量字符串建议使用数组的join方法效率更高。5.2 数组高频方法数组在前端的地位非常重要。列表渲染、数据筛选、数据聚合都离不开数组操作。下面这些方法是面试和日常开发中的常客const numbers [1, 2, 3, 4, 5]; // map映射成新数组 const doubled numbers.map(item item * 2); console.log(doubled); // [2, 4, 6, 8, 10] // filter筛选符合条件的元素 const even numbers.filter(item item % 2 0); console.log(even); // [2, 4] // reduce累加计算 const sum numbers.reduce((total, item) total item, 0); console.log(sum); // 15 // forEach遍历不返回新数组 numbers.forEach(item console.log(item)); // find找到第一个匹配项 const found numbers.find(item item 3); console.log(found); // 4再看一个更接近业务场景的例子将用户列表中的成年用户提取出来并只保留姓名和年龄字段。const users [ { name: 张三, age: 17 }, { name: 李四, age: 22 }, { name: 王五, age: 30 } ]; const adults users .filter(user user.age 18) .map(user ({ name: user.name, age: user.age })); console.log(adults); // [{ name: 李四, age: 22 }, { name: 王五, age: 30 }]这种链式调用在真实项目中非常常见既清晰又优雅。5.3 解构赋值与展开运算符解构赋值可以快速从对象或数组中提取数据const user { name: 张三, age: 20, city: 北京 }; const { name, age } user; console.log(name, age); // 张三 20 const arr [10, 20, 30]; const [first, ...rest] arr; console.log(first); // 10 console.log(rest); // [20, 30] // 展开运算符 const arr1 [1, 2]; const arr2 [3, 4]; const merged [...arr1, ...arr2]; console.log(merged); // [1, 2, 3, 4]展开运算符还常用于浅拷贝对象const newUser { ...user }。需要注意这种拷贝是浅拷贝嵌套对象仍然共享引用。6. 对象与原型链理解 JavaScript 的“类”6.1 对象的创建方式除了字面量创建对象JavaScript 还支持构造函数和class语法// 对象字面量 const obj { name: 张三 }; // 构造函数ES5 写法 function Person(name, age) { this.name name; this.age age; } Person.prototype.sayHello function () { console.log(你好我是 this.name); }; // classES6 语法 class Student extends Person { constructor(name, age, grade) { super(name, age); this.grade grade; } study() { console.log(this.name 正在学习); } } const s new Student(李四, 18, 高三); s.sayHello(); // 你好我是李四 s.study(); // 李四 正在学习class本质上是原型链的语法糖但更接近传统面向对象语言的书写方式目前主流项目都推荐使用class。6.2 原型链每个函数都有prototype属性每个对象都有__proto__属性现在推荐使用Object.getPrototypeOf。当我们访问一个对象的方法时JavaScript 会沿着原型链一层层向上查找function Animal(name) { this.name name; } Animal.prototype.speak function () { console.log(this.name 发出声音); }; const dog new Animal(旺财); dog.speak(); // 旺财 发出声音dog本身没有speak方法但通过原型链找到了Animal.prototype.speak。原型链是 JavaScript 继承机制的核心也是面试中容易遇到底层题目的出发点。7. 异步编程从回调到 Promise 再到 async/await7.1 为什么需要异步浏览器中的很多操作是耗时的比如网络请求、定时器、文件读取。如果这些操作都同步执行页面就会卡住。异步编程让主线程先去处理其他任务等耗时任务完成后再回来处理结果。最早的异步写法使用回调函数setTimeout(() { console.log(2 秒后执行); }, 2000);回调函数嵌套太多就会形成“回调地狱”可读性变差于是出现了Promise。7.2 Promise 的基本用法const request new Promise((resolve, reject) { const success true; if (success) { resolve(请求成功); } else { reject(请求失败); } }); request .then(data { console.log(data); // 请求成功 }) .catch(err { console.error(err); });Promise有三种状态pending进行中、fulfilled已成功、rejected已失败。状态一旦改变就不能再变。7.3 async/await 语法async/await是基于Promise的语法糖让异步代码看起来更像同步代码function getUserData() { return new Promise(resolve { setTimeout(() resolve({ name: 张三, age: 20 }), 1000); }); } async function fetchUser() { const user await getUserData(); console.log(user); // 1 秒后输出 { name: 张三, age: 20 } } fetchUser();注意await只能在async函数中使用。使用try/catch可以捕获异步操作中的错误async function fetchUser() { try { const user await getUserData(); console.log(user); } catch (error) { console.error(请求出错, error); } }7.4 事件循环JavaScript 是单线程语言但通过事件循环机制可以处理并发任务。同步代码先执行微任务Promise.then优先于宏任务setTimeout执行console.log(1); setTimeout(() console.log(2), 0); Promise.resolve().then(() console.log(3)); console.log(4); // 输出顺序1 - 4 - 3 - 2这个输出顺序是面试中的经典题目理解了事件循环很多执行顺序问题都能迎刃而解。8. DOM 操作让页面动起来8.1 选择元素DOM文档对象模型是 JavaScript 操作页面的桥梁。常用的选择方法document.getElementById(app); document.querySelector(.item); // 选择第一个匹配元素 document.querySelectorAll(.item); // 返回 NodeList8.2 事件绑定事件是前端交互的核心。推荐使用addEventListenerconst button document.getElementById(btn); button.addEventListener(click, function () { alert(按钮被点击了); });8.3 事件委托当列表项很多或者列表项会动态新增时给每一项单独绑定事件性能差且不好维护。事件委托利用事件冒泡机制把事件绑定到父元素上ul idtodoList li任务一/li li任务二/li /uldocument.getElementById(todoList).addEventListener(click, function (e) { if (e.target.tagName LI) { console.log(点击了, e.target.textContent); } });这种写法在动态列表场景中非常实用。8.4 关于 javascript:void(0)很多老项目里能看到hrefjavascript:void(0)这种写法目的是让链接点击后不跳转、不刷新页面只执行绑定的事件。常见的错误用法是javascript:void(o)这种乱写会导致点击后报错。现在更推荐的做法有两个一是直接使用button元素代替a链接二是给链接的click事件调用e.preventDefault()。这样可以避免在地址栏留下冗余的javascript:协议。9. 完整实战用原生 JavaScript 做一个待办清单下面我们把前面讲过的内容串起来做一个完整的待办清单Todo List小项目。这个项目会用到变量、函数、数组、对象、DOM 操作、事件监听、本地存储等知识点代码可以直接复制运行。9.1 创建项目结构js-todo/ ├── index.html ├── style.css └── main.js9.2 编写 HTML 页面!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleJavaScript 待办清单/title link relstylesheet hrefstyle.css /head body div classapp h1待办清单/h1 div classinput-area input typetext idtaskInput placeholder输入新任务按回车确认 button idaddBtn添加/button /div ul idtodoList/ul /div script srcmain.js/script /body /html9.3 编写样式* { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: Arial, sans-serif; background-color: #f0f2f5; display: flex; justify-content: center; padding-top: 60px; } .app { width: 420px; background: #fff; padding: 24px; border-radius: 12px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } h1 { font-size: 22px; margin-bottom: 16px; } .input-area { display: flex; gap: 8px; margin-bottom: 16px; } #taskInput { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; } #addBtn { padding: 8px 16px; border: none; background: #1677ff; color: #fff; border-radius: 6px; cursor: pointer; } #todoList { list-style: none; } #todoList li { display: flex; justify-content: space-between; align-items: center; padding: 10px 8px; border-bottom: 1px solid #eee; } #todoList li.done span { text-decoration: line-through; color: #999; } .delete-btn { background: transparent; border: none; color: #ff4d4f; cursor: pointer; }9.4 编写 JavaScript 逻辑// 文件路径js-todo/main.js (function () { const taskInput document.getElementById(taskInput); const addBtn document.getElementById(addBtn); const todoList document.getElementById(todoList); // 从本地存储读取任务列表如果没有则是空数组 let tasks JSON.parse(localStorage.getItem(tasks)) || []; // 保存任务列表到本地存储 function saveTasks() { localStorage.setItem(tasks, JSON.stringify(tasks)); } // 渲染任务列表 function renderTasks() { todoList.innerHTML ; tasks.forEach((task, index) { const li document.createElement(li); if (task.done) { li.classList.add(done); } const span document.createElement(span); span.textContent task.text; span.addEventListener(click, function () { tasks[index].done !tasks[index].done; saveTasks(); renderTasks(); }); const deleteBtn document.createElement(button); deleteBtn.textContent 删除; deleteBtn.className delete-btn; deleteBtn.addEventListener(click, function () { tasks.splice(index, 1); saveTasks(); renderTasks(); }); li.appendChild(span); li.appendChild(deleteBtn); todoList.appendChild(li); }); } // 添加新任务 function addTask() { const text taskInput.value.trim(); if (!text) { alert(任务内容不能为空); return; } tasks.push({ text: text, done: false }); taskInput.value ; saveTasks(); renderTasks(); } // 绑定事件 addBtn.addEventListener(click, addTask); taskInput.addEventListener(keyup, function (event) { if (event.key Enter) { addTask(); } }); // 初始化渲染 renderTasks(); })();9.5 运行与验证直接用浏览器打开index.html就可以看到页面效果。尝试以下操作验证功能输入任务内容点击“添加”按钮任务出现在列表中。点击任务文字文字出现删除线表示已完成。点击“删除”按钮任务被移除。刷新页面任务列表仍然保留说明 localStorage 生效。这个项目的代码不长但覆盖了 JavaScript 基础中最核心的部分IIFE立即执行函数、数组的增删改查、事件绑定、DOM 动态渲染、数据持久化。建议你亲手敲一遍再尝试扩展例如增加“编辑任务”或“清空已完成任务”的功能。10. 常见运行时报错与排查思路前端开发中控制台报错是最常见的信息来源。这里梳理几个高频报错及排查方向报错信息常见原因解决思路ReferenceError: xxx is not defined变量未声明或拼写错误检查变量名是否一致是否在作用域内TypeError: xxx is not a function调用了不存在的函数或变量不是函数打印变量类型确认函数是否存在Cannot read property length of undefined对象为undefined却访问了其属性先做空值判断再访问属性SyntaxError: Unexpected token代码语法错误如括号不匹配检查代码括号、引号、逗号Unexpected end of JSON inputJSON.parse解析了空字符串或非法 JSON调用JSON.parse前先校验数据平时写代码时可以遵循以下习惯来减少运行时报错使用const或let声明变量不用全局变量。访问嵌套对象属性前先做空值判断或使用可选链操作符?.。对用户输入或接口返回的数据先校验再使用。多利用浏览器控制台console.log打印中间结果逐步定位问题。11. 最佳实践写更健壮的 JavaScript11.1 开启严格模式在文件开头或函数顶部加上use strict;可以把一些静默错误转成显式报错例如给未声明的变量赋值时会直接报错避免污染全局作用域。11.2 多使用 和显式类型转换尽量使用和!进行判断。需要转换类型时显式使用Number()、String()、Boolean()不依赖隐式转换代码可读性更好也更容易排查问题。11.3 使用函数封装逻辑尽量避免把大量逻辑堆在全局作用域。将功能拆分成小函数每个函数只做一件事既方便测试也方便复用。事件绑定中如果用到异步数据也要注意回调函数的作用域问题。11.4 防抖与节流在搜索框输入、窗口缩放、滚动加载等高频事件中直接绑定处理函数会导致性能问题。防抖和节流是两种常用优化手段防抖事件停止触发后一段时间才执行。节流事件触发后一段时间内只执行一次即使事件继续触发。一个简单的防抖函数function debounce(fn, delay 300) { let timer null; return function (...args) { clearTimeout(timer); timer setTimeout(() { fn.apply(this, args); }, delay); }; }11.5 异常处理涉及网络请求、本地存储解析、用户输入时别忘了做异常处理。使用try/catch捕获同步错误使用Promise.catch或async/await配合try/catch捕获异步错误避免错误直接暴露给用户。11.6 不要阻塞主线程从互联网或本地读取数据时尽量使用异步方案。长时间运行的同步计算会阻塞页面渲染用户会看到页面“卡死”。对于复杂计算可以考虑拆分成小任务或者使用 Web Worker。12. 学习路线从快速入门到进阶如果你是零基础开始学习建议按照下面的路径推进HTML 和 CSS 基础了解页面结构和样式不需要精通能看懂就行。JavaScript 基础语法变量、类型、运算符、流程控制、函数、数组、对象。DOM 与事件学会操作页面元素和响应用户交互。异步编程理解回调、Promise、async/await理解事件循环。网络请求掌握fetch和axios的基本用法了解跨域问题。框架学习选一个主流框架Vue 或 React入门理解组件化开发。工程化基础了解 npm、模块化、构建工具Webpack 或 Vite。源码阅读有一定基础后可以尝试阅读框架源码或经典工具库源码提升深层理解。如果想找工作除了动手写项目面试题也要认真准备。前端面试中this指向、闭包、事件循环、原型链、数组方法、类型转换都是高频考点这些内容在这篇文章里基本都有涉及。建议每学一个知识点都尝试用自己的话讲给别人听同时给出对应的代码示例这比单纯刷题更能加深理解。JavaScript 是一门需要大量实践的语言。看再多的教程都不如自己在控制台里敲一遍、遇到报错再修一遍来得深刻。希望这份教程能帮你快速建立知识框架少走一些弯路接下来就可以带着这些基础去读源码、学框架、写自己的小项目了。