02变量与数据类型
JavaScript 是「弱类型」语言——变量本身没有类型,值才有类型。
同一个变量,今天装字符串、明天装数字,没人拦你。
这一课把声明方式 var / let / const 三兄弟和
7 种原始类型 + 1 个对象类型一次讲清,
把代码里"变量"和"类型"两个最容易混的概念钉在脑子里。
一、三种声明:var / let / const
声明一个变量,就是告诉浏览器"我占个坑、以后会往里塞值"。
三种声明方式各有脾气,现代项目里默认用 const,
需要改的才用 let,var 几乎绝迹。
| 关键字 | 作用域 | 可重复声明 | 变量提升 | 可修改值 | 现代态度 |
|---|---|---|---|---|---|
| var | 函数作用域 | ✓ 可以 | ✓ 提升到顶 | ✓ | 老代码遗产,新项目基本不用 |
| let | 块作用域 { } | ✗ 报错 | TDZ 暂时性死区 | ✓ | 默认值会变的变量 |
| const | 块作用域 { } | ✗ 报错 | TDZ 暂时性死区 | ✗ 不可重新赋值 | 现代项目默认值 |
const 不是说"对象不能改属性"。
const obj = { a: 1 } 之后 obj.a = 2 完全合法——
它管的是「绑定关系」,不管「对象内容」。
想让对象连属性也锁死,用 Object.freeze()。二、命名规范与提升(hoisting)
命名三选一:小驼峰 userName(变量/函数首选)、
下划线 _internal(约定为「私有」)、大驼峰 UserCard(类/构造函数)。
三个硬规矩:不能数字开头、不能用保留字、要见名知意。
变量提升(hoisting)也是个大坑:var 声明会被「提到作用域顶端」,
所以可以先使用再声明(值是 undefined);而
let / const 不提升,
在声明之前使用会进入「暂时性死区」(TDZ)直接报错。
undefined(不报错);② let 在声明前使用立即抛 ReferenceErrorlet / const 在声明前的存在窗口是"看见了但用不了"——
引擎逼你先声明再使用,从此告别"先斩后奏"带来的诡异 bug。三、原始类型 7 种 + typeof
JS 把值分成两大家族:原始类型(primitive,7 种) 和 对象类型(object)。 原始类型是「不可再拆」的原子值,对象类型是「可拆」的有结构数据。
| 类型 | 典型字面量 | typeof 返回 | 一句话 |
|---|---|---|---|
| number | 42 / 3.14 / NaN | "number" | 整数小数都算它,NaN 也算 |
| string | "前端" / 'JS' / `a` | "string" | 单引、双引、反引三种引号都成 |
| boolean | true / false | "boolean" | 是 / 否,开 / 关 |
| null | null | "object" ⚠ | 「故意为空」,开发者明确说"这里没值" |
| undefined | undefined | "undefined" | 「默认空」,未赋值时的占位 |
| symbol | Symbol("id") | "symbol" | 每次调用都生成唯一值,常用于对象 key |
| bigint | 9007199254740993n | "bigint" | 数字后面加 n,突破 number 精度上限 |
null 返回的是 "object"(JS 历史 bug)typeof null === "object" 是 JS 史上最著名的 bug:
2000 年前就存在,至今为了兼容性仍不能修。判断「是不是 null」必须用
val === null,绝不能信 typeof。四、引用类型:对象 / 数组 / 函数
除了 7 种原始值,其他全是引用类型(object): 普通对象、数组、函数、日期、正则… 它们存的不是值本身,是地址(指针)—— 复制过去的是同一张门卡,谁改都看得见。
| 值 | typeof 返回 | 类别 |
|---|---|---|
{} | "object" | 普通对象 |
[] | "object" | 数组(Array.isArray() 才是真判法) |
function(){} | "function" | 函数(对象里唯一 typeof 不是 object 的) |
null | "object" | 「空对象指针」—— 历史遗留 |
Object.assign({}, src) / {...src},或者
深拷贝 structuredClone(src)(深到嵌套对象)。
这是后面讲「引用陷阱」和「状态管理」的起点。五、字符串模板与常用方法
字符串用 ' ' / " " / ` ` 包起来都行,
但反引号(模板字符串)是唯一能直接嵌入变量和换行的写法:
| 方法 | 作用 | 示例 |
|---|---|---|
length | 取长度(属性,非方法) | "前端".length → 2 |
indexOf(s) | 查位置(找不到 -1) | "前端自习".indexOf("自习") → 2 |
slice(a, b) | 截取 a 到 b(不含 b) | "hello".slice(1,4) → "ell" |
split(s) | 按 s 切成数组 | "a,b,c".split(",") → ["a","b","c"] |
replace(a, b) | 替换首个匹配 | "hi".replace("h","H") → "Hi" |
trim() | 去首尾空白 | " hi ".trim() → "hi" |
toUpperCase() | 转大写 | "js".toUpperCase() → "JS" |
includes(s) | 是否包含 s | "前端".includes("端") → true |
startsWith(s) | 是否以 s 开头 | "https://".startsWith("http") → true |
name 默认值成你自己的名字"abc".replace("a","x") 不会改变 "abc" 本身——必须接住返回值。六、数字与数学
JS 的 number 只有一个类型——IEEE 754 双精度浮点,
整数和小数共用。这意味着浮点运算有精度陷阱:
0.1 + 0.2 不等于 0.3,而是
0.30000000000000004。
0.1+0.2 的真面目;② 取 1~100 随机整数;③ 四舍五入 4.5 / 4.4 / -1.5((0.1 * 10 + 0.2 * 10) / 10).toFixed(2)、
或装 decimal.js / big.js。
前端最常见的场景——价格展示——务必用「分」做单位、整数运算。七、类型转换:隐式 vs 显式
JS 在某些场景会自动把值「掰弯」到想要的类型,叫隐式转换;
想自己控,就用显式转换函数。理解这套规则,
就能看懂 "5" + 3 = "53" 到底是字符串还是数字。
| 场景 | 行为 | 示例 |
|---|---|---|
+ 遇字符串 | 另一边转字符串后拼接 | "5" + 3 → "53" |
- * / | 两边都转数字 | "5" - 3 → 2 |
==(双等) | 类型不同先转再比 | "5" == 5 → true(少用) |
===(三等) | 类型不同直接 false | "5" === 5 → false(推荐) |
Number(x) | 整体转数字,不能转则 NaN | Number("abc") → NaN |
String(x) | 整体转字符串 | String(42) → "42" |
Boolean(x) | 0 / "" / null / undefined / NaN → false,其它 true | Boolean("") → false |
parseInt / parseFloat | 从头读数字,读不到就停 | parseInt("42px") → 42 |
"5"+3 → "53";② "5"-3 → 2;③ Boolean("") → false;④ Number("abc") → NaN —— 把 "5" 换成 "5.5" 看 - 的浮点处理=== / !==。
== 看着省事("5" == 5 也成立),
但它的隐式转换规则有 8 条边缘情况等你踩坑。
现代代码里 三等是标配,唯一例外是和 null 比:val == null
会同时命中 null 和 undefined,是个常用惯用法。八、本章小结
- 默认用
const,需要改的才用let,var几乎不用——块作用域是底线。 - 命名用小驼峰,不能数字开头、不能用保留字、要见名知意。
let/const不提升(TDZ),声明前使用直接 ReferenceError。- 7 种原始类型 + 1 个对象类型,
typeof是诊断工具,但typeof null是 object(历史 bug)。 - 引用类型存的是地址,赋值是「共享门卡」——改一处、到处变;想互不干扰用浅/深拷贝。
- 字符串是不可变的,所有方法返回新串;反引号模板字符串是拼接主力。
number是双精度浮点,0.1 + 0.2 !== 0.3,钱用「分」算。- 类型转换:
+拼接、-算术;判断相等用===;空串/0/NaN/null/undefined 五个是「假值」。
const obj = { name: "前端", age: 2 },把 name 改成你的名字,控制台打印对象;
② 用 console.log 输出 0.1 + 0.2 的真实结果,再用 toFixed(2) 修正;
③ 写一段模板字符串问候:`你好,${name}!今天是第 ${lesson} 课`;
④ 一组类型转换:"5" + 3、"5" - 3、Boolean("")、Number("42px")、parseInt("42px"),逐个 console.log 看结果;
⑤ 试着用 slice / split / includes 处理一段真实字符串(如 "前端-基础-02-变量" 用 split("-") 切出章节号)。