变量、数据类型与运算符
变量、数据类型与运算符
这一篇把 JS 基础语法中的字面量与变量、数据类型、类型转换、运算符一口气讲完。JavaScript 是动态类型 + 弱类型语言:变量无类型前缀、类型运行期确定、运算时会自动隐式转换——这正是它与 Java 差异最集中的地方,也是坑最多的地方。
阅读方式:与 Java 相通的部分只做对照不展开,重点看「差异」和「⚠️ 坑点」。
一、差异总览
| 维度 | Java | JavaScript |
|---|---|---|
| 类型系统 | 静态类型,编译期确定 | 动态类型,运行期确定 |
| 变量声明 | int x = 1; |
let x = 1;(无类型前缀) |
| 基本类型 | 8 种基本类型 | 7 种原始类型 |
| 空值 | 只有 null |
null 和 undefined |
| 相等比较 | ==(基本类型比值,引用比地址) |
== 和 === 两种 |
| 隐式转换 | 基本没有 | 到处都有 |
| 逻辑运算 | &&/` |
二、字面量与变量
1. 字面量(literal)
字面量就是直接写在代码里的值,Java 和 JS 概念相同,只是写法略有差异。
1 | 42 // 数字字面量 |
与 Java 的差异点:
- 字符串可用三种引号(Java 只有双引号)。
- 对象、数组可直接用字面量创建(Java 要
new)。
2. 变量声明 var / let / const
JS 用 关键字 名字 = 值,没有类型前缀。
1 | let age = 18; // 对应 Java 的 int age = 18; |
var(旧语法,有坑)
- 函数作用域,不是块级作用域,会泄漏到
{}外:
1 | if (true) { |
- 变量提升(hoisting):声明被提升,赋值不动,先使用后声明得到
undefined:
1 | console.log(a); // undefined(不报错) |
- 可重复声明同名变量。
let(推荐)
- 块级作用域,与 Java 局部变量一致。
- 有暂时性死区(TDZ):声明前访问直接报错:
1 | console.log(a); // ReferenceError |
- 不可重复声明。
const(常量)
- 声明必须初始化,不能重新赋值。
- 但只锁定引用,对象/数组内容仍可改:
1 | const obj = { name: "Tom" }; |
三者对比
| 特性 | var | let | const |
|---|---|---|---|
| 作用域 | 函数 | 块 | 块 |
| 变量提升 | 是(值为 undefined) | 有 TDZ | 有 TDZ |
| 重复声明 | 允许 | 禁止 | 禁止 |
| 重新赋值 | 允许 | 允许 | 禁止 |
⚠️ Java 程序员习惯:默认用
const,确定要改用let,**不要再用var**。命名规则(驼峰、大小写敏感)与 Java 一致。
三、数据类型
JS 的值分为原始类型(primitive)和引用类型(object)。原始类型共 7 种:
| 类型 | 说明 | 示例 |
|---|---|---|
number |
数字,整数小数统一 | 1、3.14、NaN、Infinity |
string |
字符串 | "abc"、'abc'、`abc` |
boolean |
布尔 | true、false |
null |
空值 | null |
undefined |
未定义 | undefined |
symbol |
唯一标识(ES6) | Symbol() |
bigint |
大整数 | 10n |
除这 7 种外,**一切都是 object**(数组、函数、Date、普通对象等)。
1. number(无 int/double 之分)
所有数字统一为 number,内部是 64 位双精度浮点,没有 int/double/float 区分。三个特殊值:
1 | 1 / 0; // Infinity(Java 会抛异常) |
⚠️ 坑点:
NaN === NaN是false,判断要用Number.isNaN(x)。0.1 + 0.2结果不是0.3,而是0.30000000000000004(IEEE 754 浮点精度,Java 的 double 同样存在)。
2. string(三种引号 + 模板字符串)
- 单引号、双引号等价;反引号是模板字符串,用
${}插值、支持多行:
1 | let name = "Tom"; |
3. boolean
true / false。注意 "true" 是字符串不是布尔,Boolean("false") 是 true(非空字符串)。
4. null 与 undefined(重点,Java 没有)
Java 里”空”只有 null;JS 有两种:
null |
undefined |
|
|---|---|---|
| 含义 | 人为赋的空值 | 系统默认的”未定义” |
| 何时出现 | 手动 x = null |
声明未赋值、访问不存在的属性、函数无 return |
typeof |
"object" |
"undefined" |
1 | let a; // undefined |
⚠️ 记忆点:
undefined是”机器给的默认空”,null是”人给的空”。
5. symbol 与 bigint(了解即可)
1 | const s = Symbol(); // 每次调用得到唯一值,用作对象属性名 |
6. typeof 运算符
typeof 返回一个字符串表示类型(Java 无对应语法):
1 | typeof 123 // "number" |
⚠️ 两个坑:
typeof null是"object"(历史遗留 bug,实际 null 是原始类型)。typeof []是"object",判断数组用Array.isArray(arr)。
四、类型转换
JS 没有 Java 的 (int) 强转,类型转换分显式(用函数)和隐式(运算时自动)两种,后者是 JS 独有的坑。
1. 显式转换:Number() / String() / Boolean()
1 | Number("123") // 123 |
另有 parseInt("12px")(转整数,直到非数字停止)、parseFloat("3.14")。
falsy 值(转布尔为 false 的只有 8 个)
1 | false、0、-0、0n、""(空字符串)、null、undefined、NaN |
其余**一律是 true**(包括空数组 []、空对象 {}、字符串 "0"、"false")。
⚠️ Java 程序员易踩坑:Java 的
if只能接 boolean;JS 的if会用 truthy/falsy 自动判断,0、""、null、undefined、NaN都会走false分支。
2. 隐式转换(Java 基本没有,JS 到处都有)
运算时 JS 会自动把操作数转成”合适的类型”。
字符串拼接:+ 只要一边是字符串,就做拼接:
1 | "5" + 1 // "51" |
算术运算:- * / % 会把字符串转数字:
1 | "5" - 1 // 4 |
比较运算:> < 会转数字比较:
1 | "10" > 9 // true |
⚠️ 经典坑:
"5" + 1是"51"(拼接),"5" - 1是4(算术)。同样是"5",遇+变字符串、遇-变数字。
五、运算符
1. 算术运算符
| 运算符 | 含义 | 备注 |
|---|---|---|
+ |
加 / 字符串拼接 | 一边是字符串就拼接 |
- * / % |
减乘除取余 | 与 Java 相同 |
** |
幂运算 | Java 用 Math.pow,JS 无对应运算符 |
++ -- |
自增自减 | 与 Java 相同 |
1 | 2 ** 10 // 1024(2 的 10 次方) |
2. 赋值运算符
= += -= *= /= %= 与 Java 完全一致,不展开。
3. 比较运算符(重点 == vs ===)
===严格相等:类型和值都相等才为 true(推荐始终用它)。==宽松相等:先做隐式类型转换再比较(坑多)。
1 | 1 === "1" // false(类型不同) |
== 的转换规则(了解即可,实际都用 ===):
null == undefined为true(特例)。- 数字 vs 字符串:字符串转数字。
- 布尔 vs 其他:布尔转数字(
true→1,false→0)。 - 对象 vs 原始:对象转原始值。
对应的不等:!==(严格不等)、!=(宽松不等),同样**推荐用 !==**。
大小比较 > < >= <=:字符串按 Unicode 码点(字典序)比较,数字按数值比较:
1 | "apple" < "banana" // true |
⚠️ Java 程序员注意:Java 的
==对引用类型比较的是地址;JS 的==和===对对象/数组比较的也是引用(不是内容)。判断内容相等要用JSON.stringify或深比较。
4. 逻辑运算符(返回操作数本身)
JS 的 && || 返回的是操作数本身,不是 boolean(Java 一定返回 boolean):
1 | 0 || "default" // "default"(0 是 falsy,取右边的值) |
||:左边 truthy 就返回左边,否则返回右边(常用来设默认值)。&&:左边 falsy 就返回左边,否则返回右边。- 两者都有短路:结果确定后右边的表达式不再执行。
!返回 boolean;!!x是转布尔的惯用写法。
1 | let name = userInput || "匿名"; // 提供默认值 |
⚠️ 坑点:
0 || "默认"会返回"默认",因为0是 falsy。如果0是合法值,要用??(见下)。
5. Java 没有的运算符
**空值合并 ??**:只在左边是 null 或 undefined 时取右边(0、""、false 都算有值):
1 | let a = null ?? "默认"; // "默认" |
?? 与 || 的区别:
1 | let x = 0 || "默认"; // "默认"(0 是 falsy) |
**可选链 ?.**:链式访问时,任一环节是 null/undefined 就返回 undefined 而不报错(对应 Java 冗长的 null 判断):
1 | let city = user?.address?.city; // user 或 address 为空时得到 undefined,不抛错 |
**展开运算符 ...**:展开数组/对象:
1 | let arr = [1, 2, 3]; |
**三元 ?:**:与 Java 相同:
1 | let x = age >= 18 ? "成年" : "未成年"; |
其他:
1 | typeof x // 判断类型 |
六、速查表
| 需求 | 写法 |
|---|---|
| 声明变量 | let x = 1; |
| 声明常量 | const PI = 3.14; |
| 模板字符串 | `你好,${name}` |
| 判断类型 | typeof x |
| 判断数组 | Array.isArray(arr) |
| 转数字 | Number("123") / parseInt("12px") |
| 转字符串 | String(123) / 123 + "" |
| 转布尔 | Boolean(x) / !!x |
| 严格相等 | a === b / a !== b |
| 幂运算 | 2 ** 10 |
| 设默认值 | `x |
| 空值设默认 | x ?? "默认" |
| 安全链式访问 | user?.address?.city |
| 展开数组 | [...arr, 4] |
七、复习要点
- 动态类型 + 弱类型是 JS 与 Java 的根本差异:变量无类型前缀,运算时自动隐式转换。
- 声明用
let/const,**不要用var**(函数作用域、提升、可重复声明三个坑)。 - 7 种原始类型 + object;
number统一整数小数,特殊值NaN、Infinity,NaN !== NaN。 null是人赋的空、undefined是系统默认的空;typeof null === "object"。- 显式转换
Number/String/Boolean;8 个 falsy 值转布尔为false。 - 隐式转换:
+遇字符串是拼接,- * /遇字符串是算术。 - **始终用
===/!==**,别用==/!=(会隐式转换)。 - 逻辑
&&/||返回操作数本身;??只在 null/undefined 时兜底,?.安全访问。 0.1 + 0.2 !== 0.3是浮点精度问题,与 Java double 相同。- 忘了某条,回本文对应小节看示例即可。