变量、数据类型与运算符

这一篇把 JS 基础语法中的字面量与变量、数据类型、类型转换、运算符一口气讲完。JavaScript 是动态类型 + 弱类型语言:变量无类型前缀、类型运行期确定、运算时会自动隐式转换——这正是它与 Java 差异最集中的地方,也是坑最多的地方。

阅读方式:与 Java 相通的部分只做对照不展开,重点看「差异」和「⚠️ 坑点」。

一、差异总览

维度 Java JavaScript
类型系统 静态类型,编译期确定 动态类型,运行期确定
变量声明 int x = 1; let x = 1;(无类型前缀)
基本类型 8 种基本类型 7 种原始类型
空值 只有 null nullundefined
相等比较 ==(基本类型比值,引用比地址) ===== 两种
隐式转换 基本没有 到处都有
逻辑运算 &&/`

二、字面量与变量

1. 字面量(literal)

字面量就是直接写在代码里的值,Java 和 JS 概念相同,只是写法略有差异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
42            // 数字字面量
3.14 // 小数
1e3 // 指数 = 1000
0b101 // 二进制 = 5
0xFF // 十六进制 = 255
10n // bigint 大整数(后缀 n)

'hello' // 字符串:单引号
"hello" // 字符串:双引号
`hello` // 字符串:反引号(模板字符串)

true / false // 布尔字面量
null // 空值字面量

[1, 2, 3] // 数组字面量
{ name: "Tom" } // 对象字面量

与 Java 的差异点:

  • 字符串可用三种引号(Java 只有双引号)。
  • 对象、数组可直接用字面量创建(Java 要 new)。

2. 变量声明 var / let / const

JS 用 关键字 名字 = 值,没有类型前缀。

1
2
let age = 18;        // 对应 Java 的 int age = 18;
const name = "Tom"; // 对应 Java 的 final String name = "Tom";

var(旧语法,有坑)

  • 函数作用域,不是块级作用域,会泄漏到 {} 外:
1
2
3
4
if (true) {
var x = 10;
}
console.log(x); // 10(Java 会编译报错)
  • 变量提升(hoisting):声明被提升,赋值不动,先使用后声明得到 undefined
1
2
console.log(a); // undefined(不报错)
var a = 1;
  • 可重复声明同名变量。

let(推荐)

  • 块级作用域,与 Java 局部变量一致。
  • 暂时性死区(TDZ):声明前访问直接报错:
1
2
console.log(a); // ReferenceError
let a = 1;
  • 不可重复声明。

const(常量)

  • 声明必须初始化,不能重新赋值
  • 但只锁定引用,对象/数组内容仍可改:
1
2
3
const obj = { name: "Tom" };
obj.name = "Jerry"; // ✅ 改属性可以
obj = {}; // ❌ 改引用报 TypeError

三者对比

特性 var let const
作用域 函数
变量提升 是(值为 undefined) 有 TDZ 有 TDZ
重复声明 允许 禁止 禁止
重新赋值 允许 允许 禁止

⚠️ Java 程序员习惯:默认用 const,确定要改用 let,**不要再用 var**。命名规则(驼峰、大小写敏感)与 Java 一致。

三、数据类型

JS 的值分为原始类型(primitive)引用类型(object)。原始类型共 7 种:

类型 说明 示例
number 数字,整数小数统一 13.14NaNInfinity
string 字符串 "abc"'abc'`abc`
boolean 布尔 truefalse
null 空值 null
undefined 未定义 undefined
symbol 唯一标识(ES6) Symbol()
bigint 大整数 10n

除这 7 种外,**一切都是 object**(数组、函数、Date、普通对象等)。

1. number(无 int/double 之分)

所有数字统一为 number,内部是 64 位双精度浮点,没有 int/double/float 区分。三个特殊值:

1
2
3
1 / 0;        // Infinity(Java 会抛异常)
-1 / 0; // -Infinity
"abc" * 1; // NaN(Not a Number,非法运算结果)

⚠️ 坑点

  • NaN === NaNfalse,判断要用 Number.isNaN(x)
  • 0.1 + 0.2 结果不是 0.3,而是 0.30000000000000004(IEEE 754 浮点精度,Java 的 double 同样存在)。

2. string(三种引号 + 模板字符串)

  • 单引号、双引号等价;反引号是模板字符串,用 ${} 插值、支持多行:
1
2
let name = "Tom";
let s = `Hello, ${name}!`; // "Hello, Tom!"(对应 Java 的字符串拼接)

3. boolean

true / false。注意 "true" 是字符串不是布尔,Boolean("false")true(非空字符串)。

4. null 与 undefined(重点,Java 没有)

Java 里”空”只有 null;JS 有两种:

null undefined
含义 人为赋的空值 系统默认的”未定义”
何时出现 手动 x = null 声明未赋值、访问不存在的属性、函数无 return
typeof "object" "undefined"
1
2
3
4
5
6
7
let a;          // undefined
let obj = {};
obj.name; // undefined(属性不存在)
function f() {} // 无 return
f(); // undefined

let b = null; // 人为表示"无值"

⚠️ 记忆点undefined 是”机器给的默认空”,null 是”人给的空”。

5. symbol 与 bigint(了解即可)

1
2
const s = Symbol();   // 每次调用得到唯一值,用作对象属性名
const big = 10n; // 超出 number 精度的大整数

6. typeof 运算符

typeof 返回一个字符串表示类型(Java 无对应语法):

1
2
3
4
5
6
7
8
typeof 123          // "number"
typeof "abc" // "string"
typeof true // "boolean"
typeof undefined // "undefined"
typeof Symbol() // "symbol"
typeof 10n // "bigint"
typeof {} // "object"
typeof function(){} // "function"

⚠️ 两个坑

  • typeof null"object"(历史遗留 bug,实际 null 是原始类型)。
  • typeof []"object",判断数组用 Array.isArray(arr)

四、类型转换

JS 没有 Java 的 (int) 强转,类型转换分显式(用函数)和隐式(运算时自动)两种,后者是 JS 独有的坑。

1. 显式转换:Number() / String() / Boolean()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Number("123")      // 123
Number("") // 0(空字符串转 0)
Number("abc") // NaN
Number(true) // 1
Number(null) // 0
Number(undefined) // NaN

String(123) // "123"
String(true) // "true"
String(null) // "null"

Boolean("0") // true(非空字符串都是 true)
Boolean(0) // false
Boolean("") // false
Boolean([]) // true(空数组也是 true)

另有 parseInt("12px")(转整数,直到非数字停止)、parseFloat("3.14")

falsy 值(转布尔为 false 的只有 8 个)

1
false0、-00n""(空字符串)、nullundefinedNaN

其余**一律是 true**(包括空数组 []、空对象 {}、字符串 "0""false")。

⚠️ Java 程序员易踩坑:Java 的 if 只能接 boolean;JS 的 if 会用 truthy/falsy 自动判断,0""nullundefinedNaN 都会走 false 分支。

2. 隐式转换(Java 基本没有,JS 到处都有)

运算时 JS 会自动把操作数转成”合适的类型”。

字符串拼接+ 只要一边是字符串,就做拼接:

1
2
3
"5" + 1      // "51"
5 + "1" // "51"
"5" + true // "5true"

算术运算- * / % 会把字符串转数字:

1
2
3
"5" - 1      // 4
"5" * "2" // 10
"abc" - 1 // NaN

比较运算> < 会转数字比较:

1
"10" > 9     // true

⚠️ 经典坑"5" + 1"51"(拼接),"5" - 14(算术)。同样是 "5",遇 + 变字符串、遇 - 变数字。

五、运算符

1. 算术运算符

运算符 含义 备注
+ 加 / 字符串拼接 一边是字符串就拼接
- * / % 减乘除取余 与 Java 相同
** 幂运算 Java 用 Math.pow,JS 无对应运算符
++ -- 自增自减 与 Java 相同
1
2 ** 10      // 1024(2 的 10 次方)

2. 赋值运算符

= += -= *= /= %= 与 Java 完全一致,不展开。

3. 比较运算符(重点 == vs ===)

  • === 严格相等:类型和值都相等才为 true(推荐始终用它)。
  • == 宽松相等:先做隐式类型转换再比较(坑多)。
1
2
3
4
5
1 === "1"      // false(类型不同)
1 == "1" // true(字符串转数字)
true == 1 // true(布尔转数字)
null == undefined // true(唯一特例:两者互等)
"" == 0 // true

== 的转换规则(了解即可,实际都用 ===):

  1. null == undefinedtrue(特例)。
  2. 数字 vs 字符串:字符串转数字。
  3. 布尔 vs 其他:布尔转数字(true→1,false→0)。
  4. 对象 vs 原始:对象转原始值。

对应的不等:!==(严格不等)、!=(宽松不等),同样**推荐用 !==**。

大小比较 > < >= <=:字符串按 Unicode 码点(字典序)比较,数字按数值比较:

1
2
"apple" < "banana"   // true
"10" > 9 // true(隐式转数字)

⚠️ Java 程序员注意:Java 的 == 对引用类型比较的是地址;JS 的 ===== 对对象/数组比较的也是引用(不是内容)。判断内容相等要用 JSON.stringify 或深比较。

4. 逻辑运算符(返回操作数本身)

JS 的 && || 返回的是操作数本身,不是 boolean(Java 一定返回 boolean):

1
2
3
4
0 || "default"     // "default"(0 是 falsy,取右边的值)
1 && "hello" // "hello"(1 是 truthy,取右边的值)
"" || "fallback" // "fallback"
"name" && null // null
  • ||:左边 truthy 就返回左边,否则返回右边(常用来设默认值)。
  • &&:左边 falsy 就返回左边,否则返回右边。
  • 两者都有短路:结果确定后右边的表达式不再执行。
  • ! 返回 boolean;!!x 是转布尔的惯用写法。
1
2
3
let name = userInput || "匿名";   // 提供默认值
!!0 // false
!!"abc" // true

⚠️ 坑点0 || "默认" 会返回 "默认",因为 0 是 falsy。如果 0 是合法值,要用 ??(见下)。

5. Java 没有的运算符

**空值合并 ??**:只在左边是 nullundefined 时取右边(0""false 都算有值):

1
2
3
let a = null ?? "默认";   // "默认"
let b = 0 ?? "默认"; // 0(0 不是 null/undefined)
let c = "" ?? "默认"; // ""(空字符串也算有值)

??|| 的区别:

1
2
let x = 0 || "默认";   // "默认"(0 是 falsy)
let y = 0 ?? "默认"; // 0(0 不是空值)

**可选链 ?.**:链式访问时,任一环节是 null/undefined 就返回 undefined 而不报错(对应 Java 冗长的 null 判断):

1
let city = user?.address?.city;   // user 或 address 为空时得到 undefined,不抛错

**展开运算符 ...**:展开数组/对象:

1
2
3
let arr = [1, 2, 3];
let arr2 = [...arr, 4]; // [1, 2, 3, 4]
let merged = { ...obj1, ...obj2 }; // 合并对象

**三元 ?:**:与 Java 相同:

1
let x = age >= 18 ? "成年" : "未成年";

其他

1
2
3
typeof x                    // 判断类型
obj instanceof Constructor // 判断是否某构造函数的实例(同 Java instanceof)
"key" in obj // 判断对象是否有某属性

六、速查表

需求 写法
声明变量 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]

七、复习要点

  1. 动态类型 + 弱类型是 JS 与 Java 的根本差异:变量无类型前缀,运算时自动隐式转换。
  2. 声明用 let/const,**不要用 var**(函数作用域、提升、可重复声明三个坑)。
  3. 7 种原始类型 + object;number 统一整数小数,特殊值 NaNInfinityNaN !== NaN
  4. null 是人赋的空、undefined 是系统默认的空;typeof null === "object"
  5. 显式转换 Number/String/Boolean;8 个 falsy 值转布尔为 false
  6. 隐式转换:+ 遇字符串是拼接,- * / 遇字符串是算术。
  7. **始终用 ===/!==**,别用 ==/!=(会隐式转换)。
  8. 逻辑 &&/|| 返回操作数本身;?? 只在 null/undefined 时兜底,?. 安全访问。
  9. 0.1 + 0.2 !== 0.3 是浮点精度问题,与 Java double 相同。
  10. 忘了某条,回本文对应小节看示例即可。