Skip to content

第 03 章 · JavaScript 入门

一句话开篇:HTML 是骨架、CSS 是装修,JavaScript 就是给房子装上"电闸、门铃、自动门"——让网页能"动"起来、能"听话"


0. 生活类比(先建立直觉)

把网页比作一家奶茶店

角色网页对应任务
招牌、菜单、桌椅(固定的)HTML"店里有什么"
招牌颜色、桌布、灯光CSS"店里长什么样"
收银员、点单系统、自动门JavaScript"店里能干什么"

没有 JS 的网页:菜单是死的、按钮按了没反应、用户填完表单也提交不了。 有了 JS 的网页:点"加入购物车"会响应、输入手机号会校验、滚动到底部自动加载下一页。

JS 是浏览器里的"大脑"

┌─────────── 浏览器 ─────────────────┐
│   HTML(骨头) + CSS(皮肤)          │
│                                    │
│   JavaScript(大脑、神经)            │
│   - 监听用户操作(点击、输入、滚动)    │
│   - 修改页面内容(加文字、改颜色)       │
│   - 和服务器通信(取数据、发数据)       │
└────────────────────────────────────┘

1. 概念是什么

JavaScript(简称 JS)是一种动态、解释执行、单线程的脚本语言。

  • 1995 年由 Brendan Eich 用 10 天发明(这件事本身就够离谱)。
  • 标准化后叫 ECMAScript(ES),常见说"ES6"="ES2015"。
  • 一开始只能在浏览器跑,后来 Node.js 让它能在服务器跑,现在还能在小程序、桌面应用、嵌入式跑——是真正的"全家桶"语言。

和 HTML/CSS 的关系

HTML:       <button id="btn">点我</button>
CSS:        #btn { background: red; }
JavaScript: document.getElementById('btn').onclick = () => alert('Hi');

3 件套各司其职,缺一不可。


2. 为什么需要它

只有 HTML + CSS 的页面是静态的——内容固定,用户做啥它都不响应。一旦加了 JS:

没 JS有 JS
表单填完只能跳转刷新输入时实时校验、不刷新提交
图片只能挨个点开看轮播图自动切换
数据从服务器返回新的 HTML 整页刷新AJAX 局部更新(更快、更省流量)
内容写死在 HTML 里数据驱动、动态生成
静态展示SPA 单页应用、即时聊天、在线协作

简单说:JS 让网页从"画册"变成"应用"


3. 怎么用(最小示例)

html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8" /></head>
<body>
  <button id="btn">点我</button>
  <p id="out"></p>

  <script>
    let count = 0;
    document.getElementById('btn').addEventListener('click', () => {
      count++;
      document.getElementById('out').textContent = '已点击 ' + count + ' 次';
    });
  </script>
</body>
</html>

15 行代码,浏览器双击打开,点击按钮就能看到计数。


4. 进阶要点

4.1 变量(let / const / var

关键字是否可重新赋值作用域是否提升推荐度
const❌ 不可(首选)块级 {}提升但不可访问⭐⭐⭐⭐⭐
let✅ 可块级 {}提升但不可访问⭐⭐⭐⭐
var✅ 可函数级 / 全局提升且为 undefined⭐(不推荐)
js
const PI = 3.14;       // 常量,不能改
let count = 0;         // 可变变量
count = count + 1;     // ✅
// PI = 3.15;          // ❌ TypeError

var oldStyle = 1;      // 老 JS 的写法,已不推荐

口诀默认 const,需要变才用 let,永远别用 var

命名规则

  • 字母、数字、_$,不能数字开头。
  • 区分大小写:nameName 是两个变量。
  • 习惯用 小驼峰userNameisLoggedIn

4.2 数据类型

JS 有 8 种数据类型:7 种原始类型 + 1 种对象类型。

类型例子说明
number42, 3.14, NaN, Infinity数字(不分整数浮点)
string'hi', "hi", `hi ${name}`字符串(三种引号)
booleantrue, false真假
undefinedundefined声明了但没赋值
nullnull"我故意空着"
bigint9007199254740993n超大整数(加 n)
symbolSymbol('id')唯一值(高级用法)
object{}, [], function, Date引用类型

typeof 查类型(注意几个坑):

js
typeof 42           // "number"
typeof "hi"         // "string"
typeof true         // "boolean"
typeof undefined    // "undefined"
typeof null         // "object" ⚠️ 历史 bug
typeof []           // "object"  ⚠️ 数组也是 object
typeof function(){} // "function"

字符串模板(反引号 + ${}):

js
const name = '张三';
const greet = `你好,${name}!今年 ${2026 - 2000} 岁`;
// "你好,张三!今年 26 岁"

支持多行、自动换行:

js
const html = `
  <div>
    <p>${name}</p>
  </div>
`;

4.3 运算符

算术+ - * / % **% 取余,** 幂)

js
10 % 3   // 1
2 ** 10  // 1024

比较

写法含义是否类型转换
==宽松相等是 ⚠️(坑)
===严格相等(推荐)
!= / !==不等同上
>, <, >=, <=大小比较
js
1 == '1'    // true ⚠️
1 === '1'   // false ✅
null == undefined   // true ⚠️
NaN === NaN         // false ⚠️ 这是 NaN 的奇葩

铁律:永远用 ===,除非你明确需要 ==

逻辑

js
true && false   // false(与,全真才真)
true || false   // true(或,有一真就真)
!true           // false(非)

短路求值(实用技巧):

js
const name = userInput || '匿名';     // 给默认值
isReady && doSomething();              // 条件执行

const a = obj?.user?.name ?? '未知';   // 可选链 + 空值合并
运算符说明例子
?.可选链(前面是 null/undefined 就停)obj?.a?.b
??空值合并(左侧是 null/undefined 才用右侧)x ?? '默认'

??|| 区别:

js
0 || 'default'     // 'default' ⚠️ 0 也被当成"假"
0 ?? 'default'     // 0           ✅ 只有 null/undefined 才换

4.4 流程控制

if / else

js
const score = 85;
if (score >= 90)      console.log('优秀');
else if (score >= 60) console.log('及格');
else                  console.log('不及格');

三元表达式(更简洁):

js
const result = score >= 60 ? '及格' : '不及格';

switch

js
switch (day) {
  case 1:
  case 2:
  case 3:
  case 4:
  case 5:  console.log('工作日'); break;
  case 6:
  case 7:  console.log('周末');   break;
  default: console.log('错误');
}

⚠️ case 不写 break 会"穿透"到下一个 case。

循环

js
// 1. 经典 for
for (let i = 0; i < 5; i++) console.log(i);

// 2. while
let n = 0;
while (n < 5) { console.log(n); n++; }

// 3. for...of —— 遍历数组、字符串等可迭代对象(推荐)
const arr = ['a', 'b', 'c'];
for (const item of arr) console.log(item);

// 4. for...in —— 遍历对象的 key(不推荐用于数组)
const obj = { a: 1, b: 2 };
for (const key in obj) console.log(key, obj[key]);

// 5. forEach —— 数组方法
arr.forEach((item, idx) => console.log(idx, item));
循环用途能 break 吗
for万能
while不知道循环次数
for...of遍历可迭代值(数组、字符串、Set、Map)
for...in遍历对象 key
forEach遍历数组❌ 不能 break(只能 return 退出当前回调)

4.5 函数

3 种声明方式

js
// 1. 函数声明(有提升)
function add(a, b) {
  return a + b;
}

// 2. 函数表达式(赋值给变量)
const sub = function(a, b) {
  return a - b;
};

// 3. 箭头函数(最常用)
const mul = (a, b) => a * b;
const square = x => x * x;          // 单参数可省略括号
const greet = () => 'Hello';        // 无参数要写 ()
const log = msg => { console.log(msg); };  // 多行用 {}

箭头函数 vs 普通函数

维度普通函数箭头函数
this调用时决定词法(继承外层)
arguments
能当构造函数能(new不能
简洁度

默认参数 / 剩余参数 / 解构

js
function greet(name = '游客', ...rest) {
  console.log(name, rest);
}
greet();                          // 游客 []
greet('张三', 18, '北京');         // 张三 [18, '北京']

function intro({ name, age }) {   // 参数解构
  return `${name} ${age} 岁`;
}
intro({ name: '李四', age: 20 });

4.6 作用域与闭包

作用域(变量能被访问的范围):

  • 全局作用域<script> 的最外层(浏览器中是 window)。
  • 函数作用域:函数内部。
  • 块级作用域{} 内(let / const 才有,var 没有)。
js
{
  let x = 1;
  const y = 2;
  var z = 3;
}
// console.log(x); // ❌ ReferenceError
// console.log(y); // ❌
console.log(z);    // 3 ⚠️ var 漏出来了

闭包(神秘但常考):

闭包 = 函数 + 它定义时所处的环境

通俗说:函数即使在外面被调用,也能记住"老家"的变量

js
function makeCounter() {
  let count = 0;                  // 这个 count 被"记住"
  return function() {
    return ++count;
  };
}

const c1 = makeCounter();
c1();   // 1
c1();   // 2
c1();   // 3

const c2 = makeCounter();         // 全新的"老家"
c2();   // 1(独立计数)

经典坑(var + 循环 + 异步):

js
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出:3 3 3 ⚠️(i 共用一个)

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出:0 1 2 ✅(每轮独立 i)

4.7 数组(Array)

数组 = 有序的列表。

js
const arr = [1, 2, 3];
arr.length      // 3
arr[0]          // 1
arr[0] = 10;    // 改值

arr.push(4);    // 末尾加:[10,2,3,4],返回新长度
arr.pop();      // 末尾删:返回被删的 4
arr.unshift(0); // 头部加
arr.shift();    // 头部删

常用方法(高频面试点)

方法作用是否改原数组返回
push / pop末尾增/删新长度 / 删的项
shift / unshift头部删/增删的项 / 新长度
splice(i, n, ...items)任意位置增删被删的数组
slice(start, end)截取(不含 end)新数组
concat合并新数组
indexOf / includes查找索引 / 布尔
forEach(cb)遍历undefined
map(cb)映射成新数组新数组
filter(cb)筛选新数组
reduce(cb, init)累计任意
find(cb)找第一个满足条件的项项 / undefined
some(cb) / every(cb)任一/全部满足布尔
sort(cb)排序是 ⚠️数组
reverse()反转是 ⚠️数组
join('-')拼字符串字符串

链式调用很美

js
const total = [1, 2, 3, 4, 5]
  .filter(x => x % 2 === 0)        // [2, 4]
  .map(x => x * 10)                // [20, 40]
  .reduce((sum, x) => sum + x, 0); // 60

展开运算符 ...

js
const a = [1, 2, 3];
const b = [...a, 4, 5];           // [1,2,3,4,5]
const max = Math.max(...a);       // 3

const [first, ...rest] = a;       // first=1, rest=[2,3]

4.8 对象(Object)

对象 = 键值对集合(key-value)。

js
const user = {
  name: '张三',
  age: 18,
  address: {
    city: '北京',
    zip: '100000'
  },
  greet() {
    return `Hi, I'm ${this.name}`;
  }
};

user.name              // "张三"
user['name']           // 同上(key 是动态时用)
user.age = 19;         // 改
user.gender = 'male';  // 加
delete user.age;       // 删

遍历对象

js
Object.keys(user);     // ['name', 'address', 'greet']
Object.values(user);   // ['张三', {...}, ƒ]
Object.entries(user);  // [['name','张三'], ...]

for (const [key, val] of Object.entries(user)) {
  console.log(key, val);
}

对象解构

js
const { name, address: { city } } = user;
console.log(name, city);   // 张三 北京

// 改名 + 默认值
const { name: n = '匿名', age = 0 } = user;

展开拷贝 / 合并

js
const copy = { ...user };                       // 浅拷贝
const merged = { ...user, age: 99, vip: true }; // 合并并覆盖

⚠️ 对象是引用类型a = b 只是复制了引用,改 a 也会动 b。深拷贝要用 structuredClone(obj) 或 JSON 法。

js
const a = { x: 1 };
const b = a;
b.x = 100;
console.log(a.x);   // 100 ⚠️

4.9 类型转换(隐式 vs 显式)

js
// 显式
Number('42')      // 42
String(42)        // '42'
Boolean(0)        // false
parseInt('42px')  // 42
parseFloat('3.14abc')  // 3.14

// 隐式(坑王)
'5' + 3           // '53' (字符串拼接)
'5' - 3           // 2     (减法触发数字转换)
'5' * '3'         // 15
[] + []           // ''
[] + {}           // '[object Object]'
true + 1          // 2

Falsy 值(被当成 false 的 8 个):

false, 0, -0, 0n, '', null, undefined, NaN

其他都是 truthy(包括 []{}'0''false')。

js
if ([])  console.log('1'); // 输出!空数组是 truthy ⚠️
if ('0') console.log('2'); // 输出!非空字符串是 truthy

5. 实战案例

详见 examples/ 目录:

  • variables.html — 变量、数据类型、类型转换
  • functions.html — 函数 3 种写法、闭包、回调
  • array-object.html — 数组方法链 + 对象操作

完整 demo 见 demo/index.html:纯原生 JS 实现的 TodoList。


6. ⚠️ 易踩的坑

坑 1:===== 混用

==类型转换后比较,规则极其复杂;=== 不转换,所见即所得。永远用 ===

js
0 == false           // true
'' == false          // true
null == undefined    // true
NaN === NaN          // false(NaN 不等于自己)

坑 2:浮点数精度

js
0.1 + 0.2 === 0.3   // false ⚠️ 输出 0.30000000000000004

原因:二进制无法精确表示 0.1。涉及钱时用 整数(分) 或专门的库(decimal.js、big.js)。

坑 3:var 的变量提升与函数作用域

js
console.log(a);   // undefined(不是报错!)
var a = 1;

// 等价于
var a;
console.log(a);
a = 1;

let / const 也提升,但是处于"暂时性死区(TDZ)",访问会报错。这是它们更安全的原因。

坑 4:箭头函数的 this 不是动态的

js
const obj = {
  name: '张三',
  sayName: function() { console.log(this.name); },  // 张三
  sayName2: () => { console.log(this.name); }       // undefined ⚠️
};

箭头函数不绑定自己的 this,会继承外层(这里是全局)。对象方法别用箭头函数

坑 5:对象 / 数组是引用类型

js
const a = [1, 2, 3];
const b = a;
b.push(4);
console.log(a);   // [1,2,3,4] ⚠️ a 也变了

// 浅拷贝
const c = [...a];
const d = a.slice();

// 深拷贝
const e = structuredClone(a);
const f = JSON.parse(JSON.stringify(a));  // ⚠️ 函数、Date、undefined 会丢

坑 6:forEach 不能 break

js
[1, 2, 3].forEach(x => {
  if (x === 2) break;   // ❌ SyntaxError
});

// 解决:用 for...of 或 some
for (const x of [1, 2, 3]) {
  if (x === 2) break;
}

坑 7:sort 默认按字符串排序

js
[10, 1, 2].sort();         // [1, 10, 2] ⚠️ 按字符串排
[10, 1, 2].sort((a,b) => a-b);  // [1, 2, 10] ✅

坑 8:异步循环 + var

见 4.6 节闭包陷阱。永远用 let

坑 9:分号陷阱

JS 自动插入分号(ASI)有时会出错,最经典:

js
function f() {
  return
  {
    name: '张三'
  };
}
f();   // undefined ⚠️ return 后自动加了分号

对策:要返回的对象写在 return 同一行,或用 (

坑 10:typeof null === 'object'

历史遗留 bug,已经无法修了。判断 null 用 === null


7. 一句话总结

JavaScript 是浏览器里的"大脑"——记住"默认 const、永远 ===、forEach 不能 break、对象是引用、箭头函数没自己的 this",你就闯过了 80% 的入门坑。


8. 延伸阅读