第3天:JavaScript 基础(一)------ 变量·类型·运算·流程
学习时间:约 1.5 小时
目录
- [JavaScript 是什么](#JavaScript 是什么)
- 准备环境:浏览器控制台
- [变量声明:let 和 const](#变量声明:let 和 const)
- 数据类型
- 类型转换
- 运算符
- 字符串
- [流程控制:if / else / switch](#流程控制:if / else / switch)
- [循环:for / while](#循环:for / while)
- 练习
- 常见陷阱
1. JavaScript 是什么
1.1 HTML/CSS/JS 的分工
HTML = 结构(房子框架)
CSS = 装修(粉刷、家具)
JS = 水电 + 智能家居(让房子"动"起来)
你现在的网页只有 HTML + CSS,就像一张海报------好看但不能交互。JS 就是让按钮能点、数据能刷、页面能动的能力。
1.2 JS 能干什么
| 场景 | 代码示例 | 说明 |
|---|---|---|
| 点按钮弹窗 | btn.onclick = () => alert('已加入购物车') |
用户操作响应 |
| 修改页面内容 | priceTag.textContent = '¥199' |
动态更新数据 |
| 发送请求 | fetch('/api/products') |
和后端通信 |
| 表单验证 | if (phone.length !== 11) ... |
防用户输错 |
| 动画 | element.style.transform = 'rotate(90deg)' |
动态效果 |
1.3 JS 和 Java 的区别
| Java | JavaScript | |
|---|---|---|
| 运行环境 | JVM | 浏览器 / Node.js |
| 类型系统 | 强类型 | 弱类型(灵活但也容易踩坑) |
| 面向对象 | class 为主 | 原型链(ES6 也有 class) |
| 编译 | 编译执行 | 解释执行(一行一行跑) |
一句话:JS 是浏览器的"脚本语言",不用编译就能跑,写一句就能看到效果。
2. 准备环境:浏览器控制台
2.1 不用装任何东西
因为 JS 在浏览器里跑,你只需要浏览器------Chrome 最好。
2.2 打开控制台
快捷键:
Windows: F12 或 Ctrl+Shift+I
Mac: Cmd+Option+I
然后点 Console(控制台)标签
2.3 试试手
在控制台里直接输入下面内容,按回车:
js
let name = '小明'
console.log('你好,' + name)
你会看到控制台打印出"你好,小明"。
js
let price = 99
let quantity = 2
price * quantity // 直接回车,控制台会显示结果 198
控制台就是你学 JS 最好的练习场------打开 F12 就能写,不用建文件,不用编译。
2.4 在 HTML 里用 JS
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>JS 测试</title>
</head>
<body>
<button id="btn">点我</button>
<script>
// 找一个元素,绑定点击事件
const btn = document.getElementById('btn')
btn.addEventListener('click', function() {
alert('你点了我!')
})
</script>
</body>
</html>
<script> 标签就是往页面里写 JS 代码的地方。可以放在 <head> 里,也可以放在 </body> 前面(推荐放 body 最后,这样能保证先渲染页面再执行 JS)。
3. 变量声明:let 和 const
3.1 let ------ 可以变的变量
js
let price = 99
console.log(price) // 99
price = 129 // 重新赋值
console.log(price) // 129
3.2 const ------ 不能变的常量
js
const taxRate = 0.13
taxRate = 0.15 // ❌ 报错!const 不能重新赋值
// const 声明的必须立即赋值
const name // ❌ 报错!缺少初始值
原则:能用 const 就用 const,因为它让代码更安全------你不用担心它被意外改掉。
JS 新手最大的困惑:为什么 const 声明的数组还能 push?
js
const cart = []
cart.push('iPhone') // ✅ 可以!数组内容变了
cart = ['iPad'] // ❌ 报错!变量不能重新赋值
const 保证的是"变量指向的那个东西不变",不是"那个东西的内容不变"
类比:你的家庭住址不能变,但房间里住的人可以换
3.3 var ------ 已过时,别用
旧的 JS 用 var 声明变量。它有两个坑:
js
// 坑 1:没有块级作用域
if (true) {
var oldWay = '我逃出去了'
}
console.log(oldWay) // '我逃出去了' ------ 明明在 if 块里,外面也能访问
// let 就不会
if (true) {
let newWay = '我困在里面'
}
console.log(newWay) // ❌ 报错!newWay 未定义
js
// 坑 2:可以重复声明
var a = 1
var a = 2 // 不报错,覆盖了
let b = 1
let b = 2 // ❌ 报错
记住:永远用
let和const,不要用var。
3.4 命名规范
js
// ✅ 驼峰命名(camelCase)------ JS 标准
let productName = 'iPhone'
let userAge = 25
// ✅ 常量全大写
const MAX_COUNT = 100
const TAX_RATE = 0.13
// ❌ 别这样
let ProductName = 'iPhone' // 类才用大驼峰
let product_name = 'iPhone' // 这是 Python 风格
3.5 变量与 Java 的对比
java
// Java:类型写在前面
String name = "张三";
int age = 25;
double price = 99.9;
js
// JS:不需要写类型(弱类型)
let name = '张三'
let age = 25
let price = 99.9
JS 的变量没有类型标签------值才有类型。同一个变量可以先后装不同类型的值:
js
let something = 'hello' // 现在是字符串
something = 42 // 变成数字了
something = true // 又变成布尔了
这在 Java 里是禁止的,在 JS 里允许。方便但不安全,所以要自己留心变量装的是什么。
4. 数据类型
4.1 JS 类型 vs Java 类型:先看一张对照表
如果你是 Java 后端,JS 的类型体系会让你很不适应------JS 没有 byte/short/int/long/float/double 的区分,所有数字都是一种类型。
java
// Java:每个数字类型有明确的字节数和范围
byte b = 127; // 1 byte = 8 bit, -128 ~ 127
short s = 32767; // 2 bytes = 16 bit, -32768 ~ 32767
int i = 2147483647; // 4 bytes = 32 bit, -21亿 ~ 21亿
long l = 9_007_199_254_740_991L; // 8 bytes = 64 bit, -2^63 ~ 2^63-1
float f = 3.14f; // 4 bytes, IEEE 754 单精度, 约 ±3.4e38
double d = 3.14; // 8 bytes, IEEE 754 双精度, 约 ±1.8e308
js
// JS:就一种 number,相当于 Java 的 double
let price = 99.9 // 8 bytes, IEEE 754 双精度浮点数
let count = 10 // 同一个类型,只不过没有小数
4.2 基本类型(7 种)
| 类型 | 示例 | 内存 / 范围 | 说明 |
|---|---|---|---|
| number | 42、3.14、-10 |
8 bytes / ±1.8e308 | 不分整数浮点,一律 IEEE 754 双精度 |
| string | 'hello'、"你好" |
2 bytes/字符(UTF-16) | 字符串,不可变 |
| boolean | true、false |
理论 1 bit,实际因引擎而异 | 布尔 |
| null | null |
0(空引用) | 空值(主动设为空) |
| undefined | undefined |
0(未初始化) | 未定义(声明了没赋值) |
| bigint | 9007199254740991n |
按位动态分配 | 任意精度整数,n 后缀 |
| symbol | Symbol('id') |
唯一标识 | 一般用不到 |
重点 :JS 的
number= Java 的double。没有 int,没有 long,没有 float。所有的 number 都是 64 位双精度浮点数。
4.3 number ------ 深入拆解
存储模型
JS number = IEEE 754 双精度(64 bit)
┌─────────┬──────────────────┬──────────────────────────────┐
│ 1 bit │ 11 bits │ 52 bits │
│ 符号位 │ 指数位 │ 尾数位(有效数字) │
│ 0=正 1=负 │ 偏移值 1023 │ 表示有效数字 │
└─────────┴──────────────────┴──────────────────────────────┘
这解释了 JS 数字的两个关键特征:
特征 1:安全整数范围
js
// 因为尾数只有 52 bits,所以能精确表示的整数范围是有限的
Number.MAX_SAFE_INTEGER // 9007199254740991(≈ 9 千万亿,2^53 - 1)
Number.MIN_SAFE_INTEGER // -9007199254740991
// 超过这个范围,整数就不连续了
console.log(9007199254740991 + 1) // 9007199254740992 ✅
console.log(9007199254740991 + 2) // 9007199254740992 ❌ 没变!精度丢失
console.log(9007199254740991 + 3) // 9007199254740994 ✅ 跳过了 3
这和你的项目直接相关 :Snowflake ID 是 19 位数字(如 1736158234567475200),远大于 Number.MAX_SAFE_INTEGER(16 位)。这就是为什么项目中把 Long 序列化为 String------前端 JS 拿到 19 位 long 会精度丢失,最后几位变成 0。
java
// 项目已配置:所有 Long 序列化为 String
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> builder.serializerByType(Long.class, ToStringSerializer.instance);
}
特征 2:浮点数不精确
js
// 二进制无法精确表示 0.1,就像十进制无法精确表示 1/3
0.1 + 0.2 // 0.30000000000000004
// 值的范围
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324(最小的正数,不是负数最小)
常用写法
js
let count = 10
let price = 99.9 // 浮点数
let total = 3.99 * 5 // 运算
let billion = 1_000_000 // 下划线只是好看,值还是 1000000
// 进制
0xFF // 255(16进制)
0o77 // 63(8进制)
0b1101 // 13(2进制)
// 科学计数法
let mass = 1.6e-19 // 1.6 × 10^-19
// 特殊值
Infinity // 无穷大,比如 1 / 0
-Infinity // 负无穷大
NaN // Not a Number,比如 'abc' * 2
作为 Java 后端的建议 :只要你需要关心整数精度(比如 ID、订单号),就用
bigint或保持字符串传递。普通计算(金额用分单位整数)用number就够了。浮点运算精度问题所有语言都有,JS 只是最诚实的一个。但这里的 16 位安全整数限制是 JS 独有的 ------Java 的long有 19 位精度。
4.4 bigint ------ 大整数(ES2020)
当你要处理超过 2^53 的整数时,用 bigint:
js
// n 后缀声明 bigint
const snowflakeId = 1736158234567475200n
const big = 9007199254740991n + 2n // bigint 运算要加 n
// 不能混用 number 和 bigint
1n + 2n // ✅ 3n
1n + 2 // ❌ TypeError: Cannot mix BigInt and other types
// 比较可以
1n === 1 // false(类型不同)
1n == 1 // true(宽松比较)
4.3 string ------ 字符串
三种写法:
js
let s1 = '单引号'
let s2 = "双引号"
let s3 = `反引号` // 模板字符串,后面细讲
// 都一样,你选一种喜欢的就行。我推荐单引号
转义字符:
js
let msg = 'I\'m a student' // 单引号里写单引号要加反斜杠
let path = 'C:\\Users\\name' // 反斜杠本身也要转义
4.4 boolean ------ 布尔值
js
let isLoggedIn = false
let isOnSale = true
let isStockEmpty = false
4.5 null vs undefined
这是 JS 新手最容易混淆的一对:
js
let a // 声明了没赋值 → undefined
let b = null // 主动设为"空"
console.log(a) // undefined
console.log(b) // null
undefined = "还没给值"(语言的默认行为)
null = "我给清空了"(程序员的主动行为)
实际开发:
- 你不应该主动给变量赋
undefined------那是 JS 自己的事 - 你要表示"空"就用
null
4.6 typeof ------ 查看类型
js
typeof 42 // 'number'
typeof 'hello' // 'string'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof null // 'object' ← 这是一个 JS 历史遗留 bug,别管它
typeof NaN // 'number' ← 又一个坑,NaN 的类型居然是 number
遇到可疑的值,用 typeof 看一下,这是调试的常用技巧。
5. 类型转换
5.1 隐式转换 ------ JS 自作主张
JS 是弱类型语言,在运算时会自己转类型:
js
// 数字 + 字符串 → 字符串拼接
'价格:' + 99 // '价格:99'
'5' + 3 // '53'(变成了字符串)
// 字符串 - 数字 → 转成数字运算
'10' - 3 // 7
'10' * 3 // 30
'10' / 2 // 5
// 非布尔 → 布尔
if ('hello') { } // 'hello' 被当成 true(非空字符串为 true)
if (0) { } // 0 被当成 false
if ('') { } // 空字符串为 false
隐式转换的坑:
js
'5' - 3 // 2(减号触发数字转换)
'5' + 3 // '53'(加号触发字符串拼接)
// 经典坑
0 == false // true ← 0 和 false 居然相等
'' == false // true ← 空字符串也算 false
null == undefined // true ← 这两个也相等
5.2 显式转换 ------ 你说了算
js
// 转数字
Number('123') // 123
Number('12.3') // 12.3
Number('abc') // NaN(转不了)
parseInt('123px') // 123(从开头解析数字)
parseFloat('3.14') // 3.14
+ '99' // 99(加号在变量前面也是一元转换)
// 转字符串
String(123) // '123'
String(true) // 'true'
(99).toString() // '99'
// 转布尔
Boolean(1) // true
Boolean(0) // false
Boolean('') // false
Boolean('abc') // true
Boolean(null) // false
Boolean(undefined) // false
实战建议:
js
// ✅ 尽量用显式转换,少依赖隐式
let num = '42'
let total = Number(num) + 10 // 清晰:先把字符串转数字再加
// ❌ 依赖隐式容易出事
let total2 = num + 10 // '4210' ------ 字符串拼接了!
Falsy 值速记(在 if 里被当成 false 的 6 个值):
false、0、''(空字符串)、null、undefined、NaN
其他一切值都是 truthy,包括 'false' 这个字符串(因为它非空)。
6. 运算符
6.1 算术运算符
js
let a = 10
let b = 3
a + b // 13
a - b // 7
a * b // 30
a / b // 3.3333...
a % b // 1(取余数)
a ** b // 1000(10 的 3 次方)
// 简写
a += 5 // a = a + 5
a -= 3 // a = a - 3
a *= 2 // a = a * 2
6.2 自增自减
js
let count = 0
count++ // count = count + 1
count-- // count = count - 1
// 前置和后置的区别
let i = 0
console.log(i++) // 0(先取值,再加)
console.log(i) // 1
let j = 0
console.log(++j) // 1(先加,再取值)
6.3 比较运算符
js
// 基本比较
5 > 3 // true
5 < 3 // false
5 >= 5 // true
5 <= 3 // false
// 等号(重要!三个等号和两个等号)
5 == '5' // true (只比较值,不比较类型)
5 === '5' // false (比较值和类型,推荐用这个)
5 != '5' // false (只比较值)
5 !== '5' // true (比较值和类型,推荐用这个)
铁律:永远用三个 ===,不用两个 ==。
js
// 不用 == 的理由
0 == false // true
'' == false // true
null == undefined // true
' \n ' == 0 // true(空格的字符串居然等于 0)
== 的隐式转换规则非常复杂,正常人记不住。=== 就不会有这些幺蛾子------值或类型不同就是 false。
6.4 逻辑运算符
js
// &&(与):两个都为 true 才为 true
true && true // true
true && false // false
// ||(或):一个为 true 就是 true
true || false // true
false || false // false
// !(非):取反
!true // false
!false // true
!0 // true(0 是 falsy)
!'hello' // false('hello' 是 truthy)
短路求值(实用技巧):
js
// &&:前面为 false,后面就不再执行
let isLoggedIn = false
isLoggedIn && console.log('登录成功') // 不会执行,因为 isLoggedIn 是 false
// ||:前面为 true,后面就不再执行
let username = ''
let displayName = username || '匿名用户' // displayName = '匿名用户'
6.5 三元运算符
js
// 条件 ? 真 : 假
let price = 100
let label = price > 50 ? '贵' : '便宜'
console.log(label) // '贵'
等同的 if/else:
js
let label
if (price > 50) {
label = '贵'
} else {
label = '便宜'
}
三元运算符更适合"二选一赋值"的场景,写起来更简洁。
7. 字符串
7.1 字符串拼接
老的方式:
js
let name = 'AirPods Pro'
let price = 1999
// 用 + 拼接
let desc = '商品:' + name + ',价格:¥' + price
console.log(desc) // '商品:AirPods Pro,价格:¥1999'
7.2 模板字符串(强烈推荐)
用反引号(`````)+ ${} 插值:
js
let name = 'AirPods Pro'
let price = 1999
let desc = `商品:${name},价格:¥${price}`
console.log(desc) // '商品:AirPods Pro,价格:¥1999'
优势 1:不用来回 + 了
js
// 以前
let msg = '用户 ' + user + ' 在 ' + time + ' 下单了 ' + product + ',金额 ¥' + amount
// 模板字符串
let msg = `用户 ${user} 在 ${time} 下单了 ${product},金额 ¥${amount}`
优势 2:可以换行
js
// 模板字符串支持换行
let html = `
<div class="product-card">
<h3>${productName}</h3>
<p>¥${price}</p>
</div>
`
// 用引号不行------会报错
let html2 = '
<div>
' // ❌ 换行就报错
优势 3:可以写表达式
js
let price = 99
let quantity = 3
let total = `总价:¥${price * quantity}` // '总价:¥297'
7.3 常用方法
js
let str = ' Hello JavaScript '
str.length // 20(包含空格)
str.toUpperCase() // ' HELLO JAVASCRIPT '
str.toLowerCase() // ' hello javascript '
str.trim() // 'Hello JavaScript'(去首尾空格)
str.includes('Java') // true(是否包含)
str.startsWith('Hell') // false(前面有空格)
str.endsWith('pt ') // true
str.slice(2, 7) // 'Hello'(截取索引 2 到 6)
str.indexOf('Java') // 8(第几个位置)
str.replace('Java', 'Type') // ' Hello TypeScript '
str.split(' ') // ['', '', 'Hello', 'JavaScript', '', '']
// 链式调用
let phone = ' 138-1234-5678 '
let cleaned = phone.trim().replace(/-/g, '') // '13812345678'
8. 流程控制:if / else / switch
8.1 if / else
js
let stock = 0
if (stock > 100) {
console.log('库存充足')
} else if (stock > 0) {
console.log('库存紧张,仅剩 ' + stock + ' 件')
} else {
console.log('暂时缺货')
}
if 的条件判断:任何值都可以作为条件,JS 会自动转成布尔:
js
let name = ''
if (name) {
console.log('有名字') // 不会执行,空字符串是 falsy
}
let count = 0
if (count) {
console.log('有数量') // 不会执行,0 是 falsy
}
let arr = []
if (arr) {
console.log('有数组') // 会执行!空数组是 truthy!
}
注意 :空数组
[]是 truthy,空对象{}也是 truthy。很多新手在这里翻车。
8.2 switch
js
let status = 2
switch (status) {
case 1:
console.log('待付款')
break
case 2:
console.log('已付款')
break
case 3:
console.log('已发货')
break
case 4:
console.log('已完成')
break
default:
console.log('未知状态')
}
别忘了 break! 忘记的话会"穿透"到下一个 case:
js
let level = 'A'
switch (level) {
case 'A':
console.log('优秀')
// 忘记 break ------ 继续往下走
case 'B':
console.log('良好')
break
}
// 输出:
// '优秀'
// '良好' ← 这个就是穿透,不是你想要的
8.3 if vs switch
js
// if:适合范围判断(>、<、包含)
if (score >= 90) {
console.log('优秀')
} else if (score >= 60) {
console.log('及格')
} else {
console.log('不及格')
}
// switch:适合精确匹配(===)
switch (paymentMethod) {
case 'wechat':
console.log('微信支付')
break
case 'alipay':
console.log('支付宝')
break
}
9. 循环:for / while
9.1 for 循环
js
// 语法:for (初始化; 条件; 更新)
for (let i = 0; i < 5; i++) {
console.log('第 ' + (i + 1) + ' 次循环')
}
// 输出:第 1 次循环 第 2 次循环 ... 第 5 次循环
执行顺序:
第 1 步:let i = 0 → 只执行一次
第 2 步:i < 5 ? → true 执行循环体,false 退出
第 3 步:执行循环体 { ... }
第 4 步:i++ → i 变成 1
第 5 步:回到第 2 步
遍历数组:
js
let products = ['iPhone', 'iPad', 'MacBook']
for (let i = 0; i < products.length; i++) {
console.log(products[i])
}
// iPhone
// iPad
// MacBook
9.2 while 循环
js
// while:先判断再执行
let i = 0
while (i < 5) {
console.log(i)
i++
}
// do...while:先执行再判断(至少执行一次)
let j = 0
do {
console.log(j)
j++
} while (j < 5)
for vs while:
js
// 知道要循环几次 → for
for (let i = 0; i < products.length; i++) { ... }
// 不知道循环几次,看条件 → while
let stock = 100
while (stock > 0) {
sellOne() // 卖出一件
stock--
}
9.3 break 和 continue
js
// break:跳出循环
for (let i = 0; i < 10; i++) {
if (i === 5) {
break // 到 5 就停了
}
console.log(i)
}
// 输出:0 1 2 3 4
// continue:跳过本次,继续下一次
for (let i = 0; i < 5; i++) {
if (i === 2) {
continue // 跳过 2
}
console.log(i)
}
// 输出:0 1 3 4
10. 练习
练习 1(初级):在控制台玩变量
打开 F12 控制台,依次输入:
js
// 1. 声明商品名称和价格
let productName = '无线蓝牙耳机'
let price = 299
// 2. 计算打折后的价格(8折)
let discountPrice = price * 0.8
console.log(`原价:¥${price},折后价:¥${discountPrice}`)
// 3. 判断并输出
let stock = 5
if (stock > 0) {
console.log(`${productName} 有货`)
} else {
console.log(`${productName} 缺货`)
}
练习 2(初级:if / else + 三元运算)
商场促销规则:
- 满 200 打 8 折
- 满 100 打 9 折
- 否则不打折
js
let amount = 260
// 用 if/else 实现
// 试着再用三元实现
练习 3(中级:循环)
用 for 循环生成一个商品列表字符串:
js
let products = [
{ name: 'iPhone', price: 7999 },
{ name: 'iPad', price: 3499 },
{ name: 'AirPods', price: 1999 }
]
// 用 for 循环生成格式化的字符串输出:
// 1. iPhone - ¥7999
// 2. iPad - ¥3499
// 3. AirPods - ¥1999
练习 4(中级:综合)
写一个函数,判断用户登录状态 + 购物车状态,输出提示:
js
let isLoggedIn = true
let cartItems = 0
// 输出规则:
// - 未登录 → "请先登录"
// - 已登录 + 购物车为空 → "购物车是空的"
// - 已登录 + 有商品 → "您有 X 件商品在购物车"
11. 常见陷阱
陷阱 1:== 用了两个等号
js
// 永远用 ===
'5' == 5 // true(隐式转换)
'5' === 5 // false
0 == false // true
0 === false // false
陷阱 2:忘记 const 不能重新赋值
js
const price = 99
price = 100 // ❌ TypeError
// 但如果 const 的是对象,可以改里面的属性
const product = { name: 'iPhone' }
product.name = 'iPad' // ✅ 可以
product = {} // ❌ 不可以
陷阱 3:浮点数计算不精确
js
0.1 + 0.2 // 0.30000000000000004(不是 0.3!)
// 这不是 JS 的锅,所有编程语言都这样(二进制无法精确表示 0.1)
// 解决方案:金额相关用整数(分)或 toFixed
(0.1 + 0.2).toFixed(1) // '0.3'
陷阱 4:NaN 不自等
js
NaN === NaN // false ← NaN 不等于任何东西,包括自己
// 判断是不是 NaN
isNaN(NaN) // true
Number.isNaN(NaN) // true(推荐)
陷阱 5:for 循环里用了 const
js
// ❌ 这样不行
for (const i = 0; i < 5; i++) { // i++ 会报错,因为 const 不能变
console.log(i)
}
// ✅ 用 let
for (let i = 0; i < 5; i++) {
console.log(i)
}
附:今日速查
js
// 变量
let name = '值'
const MAX = 100
// 类型
typeof x // 查看类型
Number(s) // 转数字
String(n) // 转字符串
Boolean(x) // 转布尔
// 比较(永远用三个等号)
=== !==
// 字符串
`模板 ${表达式}`
str.length
str.trim()
str.includes('x')
// if
if (...) { } else if (...) { } else { }
// 循环
for (let i = 0; i < n; i++) { }
while (条件) { }
break // 跳出
continue // 跳过本次
// falsy 值(6个)
false, 0, '', null, undefined, NaN