1. 变量声明
let / const / var 的区别是面试必考题
| 关键字 | 作用域 | 可重新赋值 | 用途 |
|---|---|---|---|
var | 函数级(会变量提升) | 可以 | 旧代码,现在少用 |
let | 块级 {} | 可以 | ✅ 推荐:会变的量 |
const | 块级 {} | 不可以(指向不变) | ✅ 推荐:不变/对象 |
// 命名规则:camelCase 驼峰式
let count = 0;
count = 1; // ✅ 可改
const PI = 3.14;
PI = 3; // ❌ 报错:不能重新赋值
// 但 const 对象内部属性可以改
const user = { name: 'Tom' };
user.name = 'Jerry'; // ✅ 合法,改的是属性
要点:默认都用
const,需要更改变量值时再用 let。避免使用 var。2. 数据类型(8 种)
7 种原始类型 + 1 种引用类型
| 类型 | 说明 | 示例 |
|---|---|---|
number | 整数和浮点都用它 | 42 3.14 NaN |
string | 字符串(' ' 或 " " 或 ` `) | 'hi' |
boolean | 布尔 | true false |
undefined | 声明但未赋值 | let a; |
null | 手动置空 | let a = null |
bigint | 大整数 | 10n |
symbol | 唯一值 | Symbol('x') |
object | 引用类型(含数组、函数) | {} [] |
typeof 42; // "number"
typeof 'x'; // "string"
typeof []; // "object"(数组也是对象)
typeof null; // "object"(JS 历史遗留 bug)
typeof function(){}; // "function"
常见坑:
undefined == null 为 true,但 === 为 false。判断空值用 == null,两者都捕获。3. 运算符
重点掌握「严格相等」和「模板字符串」
相等比较(最重要的区别)
1 == '1'; // true (== 会做类型转换)
1 === '1'; // false (=== 严格,不转换)✅ 推荐
逻辑 / 三元运算
const age = 20;
// 三元:条件 ? 真 : 假
const label = age >= 18 ? '成年' : '未成年';
// ?? 空值合并:null/undefined 时才用默认值(0、'' 不会)
const n = 0 ?? 10; // 0 (0 有效值)
// 而 || 只要「假值」就用默认值
const m = 0 || 10; // 10
模板字符串
const name = 'Tom';
console.log(`Hello, ${name}! 你有 ${18+2} 岁。`);
// 用反引号 `,插入变量用 ${...}
4. 流程控制
if / switch 条件
if (score >= 90) {
'A';
} else if (score >= 60) {
'B';
} else {
'C';
}
switch (day) {
case 1: '周一'; break;
case 2: '周二'; break;
default: '其他';
}
循环:for / while / for...of
for (let i=0; i<3; i++) { /* 3 次 */ }
while (cond) { /* 条件为真则循环 */ }
// ✅ 最常用:遍历数组元素
for (const item of arr) { /* 每次得到元素 */ }
要点:遍历数组首选
for...of;遍历对象属性用 for...in(少用)。5. 函数
四种写法,理解 this 与箭头函数
四种声明方式
// 1. 函数声明(会提升)
function add(a, b) { return a + b; }
// 2. 函数表达式
const add2 = function(a, b) { return a + b; };
// 3. 箭头函数(更简洁,推荐)✅
const add3 = (a, b) => a + b;
// 单参数可省略括号
const double = x => x * 2;
// 4. 立即执行函数 IIFE
(function(x){ console.log(x); })(5);
关键区别:
箭头函数没有自己的
普通函数
this箭头函数没有自己的
this,会继承外面作用域的 this。普通函数
this 取决于调用方式。回调场景优先用箭头函数避免 this 丢失。6. 数组 & 常用方法
map / filter / reduce 是重点中的重点
const nums = [1, 2, 3, 4];
// map:每个元素变换成长度相等的新数组
const doubled = nums.map(n => n * 2); // [2,4,6,8]
// filter:筛选出满足条件的
const even = nums.filter(n => n % 2 === 0); // [2,4]
// reduce:累加/归约为单个值
const sum = nums.reduce((acc, n) => acc + n, 0); // 10
// 其他常用
nums.push(5); // 末尾加
nums.pop(); // 末尾删
arr.indexOf(x); // 查找下标,无返回 -1
arr.includes(x); // 是否存在 → true/false
arr.slice(1,3); // 截取(不改原数组)
要点:
-
map / filter / includes / find 返回新值不清脆地改原数组(不可变)。
- push / pop / sort / splice 会修改原数组(可变)。7. 对象
键值对,访问与解构
const user = {
name: 'Tom',
age: 20,
greet(){ 'hi ' + this.name; } // 简写方法
};
// 访问两种方式
user.name; // 点号
user['age']; // 中括号(变量作键时要用)
// ✅ 解构赋值(超常用)
const { name, age } = user; // 取出 name, age
// 展开运算
const clone = { ...user }; // 浅拷贝
对象是引用类型:赋值传递的是地址。复制给另一个变量后改一个,两个都变。用
{...obj} 做浅拷贝。8. 字符串常用方法
'Hello'.length; // 5
'Hello'[0]; // 'H'
'a,b,c'.split(','); // ['a','b','c']
['a','b'].join('-'); // 'a-b'
' Hello '.trim(); // 'Hello'
'hello'.toUpperCase(); // 'HELLO'
'abc'.includes('b'); // true
'Hello'.replace('l','L'); // 'HeLlo'(只换第一个)
9. DOM 与事件(详细篇)
操作网页元素 + 监听用户交互 —— 前端开发的"手脚"
9.1 什么是 DOM
DOM(Document Object Model):把 HTML 页面抽象成一颗「节点树」。浏览器把每个标签变成一个 JS 对象,JS 就能用这些对象改写页面。只要页面一打开,document 就代表整份文档。
// 页面结构(HTML)
<div id="app">
<h1>标题</h1>
<button id="btn">点我</button>
</div>
// 对应 DOM 树:document → div#app → (h1, button)
// JS 通过 document 一层层拿到元素对象
9.2 选择元素(重中之重)
| 方法 | 作用 | 返回 | 推荐度 |
|---|---|---|---|
getElementById('id') | 按 id 找 | 单个元素 | ✅ |
getElementsByClassName('cls') | 按类名找 | 类数组(HTMLCollection) | 一般 |
getElementsByTagName('li') | 按标签找 | 类数组 | 一般 |
querySelector('div .cls') | CSS选择器,找第一个 | 单个元素或null | ✅✅ 首选 |
querySelectorAll('.cls') | CSS选择器,所有 | NodeList(可forEach) | ✅✅ 首选 |
const box = document.getElementById('box'); // 按 id
const active = document.querySelector('.active'); // 第一个匹配,没有→null
const allLi = document.querySelectorAll('ul li'); // 全部,可用 forEach
allLi.forEach(li => console.log(li.textContent)); // NodeList 支持 forEach ✅
推荐:一律用
querySelector / querySelectorAll,一套 CSS 选择器语法通吃,比 getElementById 系列更灵活。9.3 修改内容 / 属性 / 样式 / 类名
// ① 内容
el.textContent = '纯文本'; // 显示为普通文字(安全)
el.innerHTML = '<b>加粗</b>'; // 解析成 HTML(慎重,防XSS)
// ② 属性
el.href = 'https://ex.com'; // 改链接
el.src = 'img.png'; // 改图片
el.setAttribute('title', '提示'); // 设任意属性
el.value = '输入框的值'; // 表单取值/赋值
// ③ 样式(内联风格)
el.style.color = 'red';
el.style.fontSize = '20px'; // CSS 请转驼峰:font-size→fontSize
// ④ 类名(d lassList 操作最佳)
el.classList.add('active');
el.classList.remove('active');
el.classList.toggle('active'); // 有就删、没有就加(做开关超好用)
el.classList.contains('active'); // 是否包含 → true/false
9.4 创建 / 添加 / 删除元素
// 新建元素 → 插入某处 → 删除
const li = document.createElement('li');
li.textContent = '新增项';
const ul = document.querySelector('ul');
ul.appendChild(li); // 加到最后
ul.prepend(li); // 加到最前
ul.appendChild(li); // 若 li 已存在,会【移动】而非复制
li.remove(); // 直接删除元素(现代写法)
ul.removeChild(li); // 传统写法:指定子元素
// 整块插入(模板字符串 + 配合字符串按钮)
ul.innerHTML += '<li>新内容</li>'; // ⚠️会重建整个里面所有内容,永别用做绑定事件
注意:
innerHTML += 会把整个元素重新解析一遍,里面已绑的事件会丢。给动态列表加项,用 createElement 更稳。9.5 事件监听:三种写法
| 写法 | 代码 | 评价 |
|---|---|---|
| ① HTML 属性(旧) | <button onclick="fn()"> | ❌ 不推荐,HTML 和 JS 混一起 |
| ② 属性赋值 | btn.onclick = fn | 只能绑一个,晚绑的覆盖 |
| ③ addEventListener ✅ | btn.addEventListener('click', fn) | ✅ 可同时绑多个,推荐 |
const btn = document.querySelector('#btn');
btn.addEventListener('click', function(e){
console.log('第一次点击');
});
btn.addEventListener('click', function(e){
console.log('第二次点击(都执行)');
});
// 移除监听要引同一函数引用
btn.removeEventListener('click', fn);
9.6 常用事件分类
| 类型 | 事件 | 触发时机 |
|---|---|---|
| 鼠标 | click dblclick mouseover mouseout mouseenter mousemove | 点击/悬停/移动 |
| 键盘 | keydown keyup | 按下/松开按键 |
| 表单 | input change submit focus blur | 输入、变化、提交、聚焦、失焦 |
| 窗口/文档 | load DOMContentLoaded resize scroll | 加载完成、窗口大小、滚动 |
点睛:
DOMContentLoaded 在 DOM 结构就绪就触发(不用等图片),常用它包住初始化代码,保证元素已存在。input 每敲一字都触发,做搜索实时过滤神器。9.7 事件对象 event
监听函数的第一个参数就是事件对象,里面装了大量事件细节。
btn.addEventListener('click', function(e){
e.target; // 实际被点到的元素(用于事件委托)
e.currentTarget; // 绑监听的那个元素
e.preventDefault(); // 阻止默认行为(如跳转、提交表单刷新)
e.stopPropagation(); // 阻止事件冒泡向上传播
});
9.8 事件冒泡 vs 捕获 + 事件委托(重点)
事件冒泡(默认):从一个元素触发后,会逐级向上传递到父级,直到
document。利用这一点,可以只给父级绑一次事件,用 e.target 判断点到了谁 —— 这叫事件委托,对动态新增元素特别有用。// 事件委托:给 ul 绑一次监听,管住所有 li(包括以后新增的)
const ul = document.querySelector('ul');
ul.addEventListener('click', function(e){
if(e.target.tagName === 'LI'){ // 判断点的是不是 li
console.log('点击了: ' + e.target.textContent);
}
});
// 之后新加的 li 也自动有响应,不用再单独绑 ✅
为什么要委托:新增元素你没办法挨个绑;且委托只需一个监听器,性能更好。手机列表、动态渲染的长列表大量用它。
9. 异步编程
Promise / async / await 是现代 JS 核心难点
Promise 基础
const p = new Promise((resolve, reject) => {
setTimeout(() => resolve('成功'), 1000);
// 失败时: reject(new Error('失败了'))
});
p.then(msg => console.log(msg)) // 成功回调
.catch(err => console.log(err)); // 失败回调
async / await(推荐写法 ✅)
async function fetchData() {
try {
const res = await fetch('https://api.example.com');
const data = await res.json();
return data;
} catch (err) {
console.log('出错:', err);
}
}
要点:
-
async 函数内用 await 等待 Promise,代码像同步一样直观。
- 用 try / catch 抓错误。
- 线程安全:JS 是单线程,异步靠事件循环(Event Loop)实现。10. ES6+ 常用新特性
现代开发每天都在用
let / const— 块级作用域变量(上面已讲)- 箭头函数 + 默认参数:
const f = (a=1, b=2) => a+b - 模板字符串:反引号 +
${} - 解构:
const {a} = obj和const [x,y] = arr - 展开运算符
...:复制数组/合并 - 类 class:语法糖,本质还是对象和原型
- 模块 import/export:代码拆分与复用
- 可选链
?.:安全访问,obj?.a?.b为空不会报错
// 可选链 & 空值合并(省去很多 if)
const name = user?.profile?.name ?? '匿名';
// 数组复制与合并
const a = [1], b = [2, ...a]; // b = [2,1]
// 对象展开浅拷贝
const copy = { ...; };
11. 动手实验
点击按钮运行效果
(未运行)
// 尝试修改下面的代码,看看 map / filter / reduce
const data = [1, 2, 3, 4, 5];
const result = data
.filter(n => n % 2 === 1) // 取奇数
.map(n => n * 10) // 乘 10
.reduce((sum, n) => sum + n, 0); // 求和
document.getElementById('out').textContent = '结果 = ' + result;