前言
在JavaScript中,我们可以分成两种类型:
- 基本类型
- 复杂类型
两种类型的区别是:存储位置不同
一、基本类型
number
null
string
undefined
boolean
symbol
1. number(数字类型)
number 是 JavaScript 中用于表示「整数」和「浮点数」的基本类型,所有数值均以 64 位双精度浮点数 格式存储(即不区分整数和小数,统一按浮点数处理)。
核心特性
- 可表示整数、小数、科学计数法数值;
- 包含特殊值:
NaN、Infinity、-Infinity。
示例代码
javascript
运行
// 1. 整数
let intNum = 10;
// 2. 浮点数
let floatNum = 3.14;
// 3. 科学计数法(等同于 12300)
let sciNum = 1.23e4;
// 4. 特殊值:NaN(Not a Number,非数字)
let nanResult = "abc" * 2; // 字符串与数字运算,结果为 NaN
// 5. 特殊值:Infinity(正无穷)、-Infinity(负无穷)
let infinityNum = 1e1000; // 超出最大可表示数值,结果为 Infinity
注意点
NaN与任何值(包括自身)比较都不相等,需用isNaN()函数判断是否为非数字;- 64 位浮点数无法精确表示某些小数(如
0.1 + 0.2 = 0.30000000000000004),需通过toFixed()或第三方库处理精度问题。
2. null(空值类型)
null 是一个特殊的基本类型,表示「空的对象引用」,语义上用于明确表示 “变量期望指向一个对象,但目前没有值”(即 “无对象” 状态)。
核心特性
- 唯一值为
null; - typeof 检测结果为
object(JavaScript 早期设计遗留问题,本质仍是基本类型)。
示例代码
javascript
运行
// 声明一个变量,明确其后续可能指向对象,初始值设为 null
let user = null;
// 后续可赋值为对象
user = { name: "Alice", age: 25 };
// 检测类型(注意:typeof null 返回 "object",需特殊判断)
console.log(typeof user); // 赋值对象后,输出 "object"
console.log(user === null); // 判断是否为空值,输出 false(此时 user 已指向对象)
注意点
- 不要与
undefined混淆:null是 “主动赋值的空”,undefined是 “未赋值的未定义”; - 常用于函数参数,表示 “未传入有效对象”,或作为函数返回值,表示 “无结果”。
3. string(字符串类型)
string 用于表示「文本数据」,是由零个或多个 Unicode 字符组成的不可变序列(一旦创建,字符串内容无法修改,修改时会创建新字符串)。
核心特性
- 可用单引号
'、双引号"、模板字面量`创建; - 模板字面量支持多行文本和表达式嵌入(
${表达式}); - 不可变性:任何字符串操作(如
slice、replace)都会返回新字符串,原字符串不变。
示例代码
javascript
运行
// 1. 单引号创建
let str1 = 'Hello';
// 2. 双引号创建
let str2 = "World";
// 3. 模板字面量(支持多行和表达式)
let name = "Bob";
let greeting = `Hello, ${name}!
Today is a nice day.`; // 多行文本无需拼接
console.log(greeting);
// 输出:
// Hello, Bob!
// Today is a nice day.
// 4. 字符串不可变(原字符串 str1 未改变,返回新字符串)
let newStr = str1.slice(0, 3);
console.log(str1); // 输出 "Hello"
console.log(newStr); // 输出 "Hel"
常用 API
length:获取字符串长度("abc".length→ 3);includes(substr):判断是否包含子串("abc".includes("ab")→ true);split(separator):按分隔符分割为数组("a,b,c".split(",")→["a","b","c"])。
4. undefined(未定义类型)
undefined 表示「变量已声明但未赋值」的状态,或 “函数无显式返回值” 时的默认返回值,语义上是 “值不存在”。
核心特性
- 唯一值为
undefined; - 无需主动赋值,变量声明后未赋值即默认是
undefined。
示例代码
javascript
运行
// 1. 变量声明未赋值,默认是 undefined
let unassignedVar;
console.log(unassignedVar); // 输出 undefined
// 2. 函数无显式返回值,默认返回 undefined
function noReturnFunc() {
console.log("我没有 return 语句");
}
let funcResult = noReturnFunc();
console.log(funcResult); // 输出 undefined
// 3. 对象未定义的属性,值为 undefined
let obj = { name: "Charlie" };
console.log(obj.age); // 输出 undefined
注意点
- 避免主动赋值
undefined(如let a = undefined),应使用null表示 “主动空值”; typeof undefined返回"undefined",可用于判断变量是否未赋值(但需注意 “变量未声明” 时用typeof也返回"undefined",需结合上下文判断)。
5. boolean(布尔类型)
boolean 是用于「逻辑判断」的基本类型,只有两个值:true(真)和 false(假),常用于条件语句(if、while)、逻辑运算(&&、||、!)。
核心特性
- 唯一值为
true和false; - 其他类型可通过「强制类型转换」转为布尔值(
Boolean(值)或!!值)。
示例代码
javascript
运行
// 1. 直接声明布尔值
let isLogin = true;
let hasPermission = false;
// 2. 条件语句中使用
if (isLogin) {
console.log("用户已登录");
}
// 3. 强制类型转换(哪些值会转为 false?)
// 「假值(Falsy)」:false、0、""(空字符串)、null、undefined、NaN
// 「真值(Truthy)」:除假值外的所有值(包括 1、"abc"、{}、[] 等)
console.log(Boolean(0)); // 输出 false
console.log(Boolean("hello")); // 输出 true
console.log(!!null); // 双重非运算,等同于 Boolean(null),输出 false
注意点
- 逻辑运算
&&和||是 “短路运算”:a && b若a为假则直接返回a,a || b若a为真则直接返回a; - 避免直接用
==比较布尔值(如if (isLogin == true)),直接写if (isLogin)更简洁。
6. symbol(符号类型)
symbol 是 ES6 新增的基本类型,用于创建「唯一的标识符」,每个 symbol 实例都是唯一的(即使描述相同,也不相等),常用于对象属性名以避免冲突。
核心特性
- 通过
Symbol(描述)创建,描述仅用于调试,不影响唯一性; - 不能与其他类型进行运算(如
symbol + 1会报错); - 作为对象属性名时,需用方括号
[]包裹,且不会被for...in、Object.keys()遍历到(可通过Object.getOwnPropertySymbols()获取)。
示例代码
javascript
运行
// 1. 创建 symbol(描述相同,实例不同)
let sym1 = Symbol("id");
let sym2 = Symbol("id");
console.log(sym1 === sym2); // 输出 false(唯一特性)
// 2. 作为对象属性名(避免属性冲突)
const user = {
name: "Dave",
[sym1]: 1001, // 用 symbol 作为属性名,需用 [] 包裹
[sym2]: 1002
};
console.log(user[sym1]); // 输出 1001(需通过 symbol 访问)
// 3. 遍历 symbol 属性(普通遍历无法获取)
console.log(Object.keys(user)); // 输出 ["name"](不包含 symbol 属性)
console.log(Object.getOwnPropertySymbols(user)); // 输出 [Symbol(id), Symbol(id)]
注意点
Symbol.for(描述)可创建 “全局共享的 symbol”(相同描述会返回同一个实例),区别于Symbol(描述)的 “局部唯一”;- 常用于框架 / 库的内部属性标记,或定义对象的 “私有属性”(非严格私有,但可避免外部误操作)。
基本类型总结表
| 类型 | 取值范围 | 核心特点 | typeof 检测结果 |
|---|---|---|---|
number | 整数、浮点数、NaN、Infinity | 64 位双精度浮点数,可表示正负无穷和非数字 | "number" |
null | 仅 null | 空对象引用,typeof 误判为 object | "object" |
string | Unicode 字符序列 | 不可变,支持模板字面量和表达式嵌入 | "string" |
undefined | 仅 undefined | 未赋值状态,函数无返回值时默认返回 | "undefined" |
boolean | true、false | 逻辑判断,可通过强制转换获取真假值 | "boolean" |
symbol | 唯一标识符(Symbol(描述) 创建) | 实例唯一,常用于对象属性名避免冲突 | "symbol" |
二、引用类型
复杂类型统称为Object,我们这里主要讲述下面三种:
- Object
- Array
- Function
一、Object(普通对象)
Object 是引用类型的 “基类”,用于存储键值对(key-value) 形式的数据,是构建其他复杂类型的基础。
1. 常用创建方式:对象字面量(最简洁)
对象字面量用 {} 包裹键值对,语法灵活,是实际开发中最常用的创建方式。
javascript
运行
let person = {
name: "Nicholas", // 键名可省略引号(符合标识符规则时,如字母/数字/下划线开头)
"age": 29, // 键名含特殊字符(如空格、连字符)或需保留关键字时,必须用字符串引号
5: true // 键名为数值时,会自动转为字符串(访问时可用 person[5] 或 person["5"])
};
2. 核心特性
-
键的类型:默认是字符串(数值键会自动转字符串,
Symbol键可用于避免属性冲突); -
值的类型:支持任意类型(基本类型如
string/number,引用类型如Array/Function); -
动态操作:创建后可随时添加、修改、删除属性:
javascript
运行
// 新增属性 person.gender = "male"; // 修改属性 person.age = 30; // 删除属性(用 delete 关键字) delete person[5]; // 访问属性(点语法或方括号语法) console.log(person.name); // 点语法:输出 "Nicholas" console.log(person["age"]); // 方括号语法:输出 30
二、Array(数组)
Array 是专门用于存储有序数据集合的引用类型,与其他语言的数组相比,JavaScript 数组更灵活(支持混合类型、动态扩容)。
1. 核心特性(你的示例已体现)
-
混合类型存储:每个槽位可存任意类型数据,无需统一类型;
-
动态大小:无需预先指定长度,添加数据时会自动扩容。
javascript
运行
// 混合类型数组(字符串、数值、对象)
let colors = ["red", 2, { age: 20 }];
// 动态添加数据(push() 方法在数组末尾添加元素)
colors.push(2);
console.log(colors); // 输出:["red", 2, {age: 20}, 2]
2. 常用操作补充
除 push() 外,数组还有大量实用方法,覆盖 “增删改查、遍历、转换” 等场景:
| 方法 | 功能描述 | 示例 |
|---|---|---|
pop() | 删除末尾元素,返回删除值 | colors.pop(); // 移除最后一个 2 |
shift() | 删除开头元素,返回删除值 | colors.shift(); // 移除 "red" |
map() | 遍历并返回新数组(数据转换) | const nums = [1,2]; nums.map(n => n*2); // [2,4] |
filter() | 筛选符合条件的元素,返回新数组 | const even = [1,2,3].filter(n => n%2===0); // [2] |
3. 注意点
- 数组的 “索引” 本质是字符串键(如
arr[0]等同于arr["0"]),但length属性会自动根据最大数字索引更新; - 避免手动修改
length(如colors.length = 2会截断数组,丢失后续元素)。
三、Function(函数)
函数是封装可执行代码块的引用类型,本质是 Function 类型的实例,因此也有属性和方法(如 name 属性、call() 方法)。函数是 JavaScript 的 “一等公民”,可作为变量、参数、返回值传递。
1. 三种常见声明方式(你的示例已覆盖)
(1)函数声明(函数提升,可提前调用)
javascript
运行
// 函数声明:function 关键字 + 函数名 + 参数 + 代码块
function sum(num1, num2) {
return num1 + num2;
}
// 可在声明前调用(函数提升特性)
console.log(sum(1, 2)); // 输出 3
(2)函数表达式(无提升,需先声明后调用)
javascript
运行
// 函数表达式:将匿名函数赋值给变量
let sum = function(num1, num2) {
return num1 + num2;
};
// 必须在赋值后调用(否则报错)
console.log(sum(3, 4)); // 输出 7
(3)箭头函数(ES6 新增,简化语法,无独立 this)
javascript
运行
// 箭头函数:(参数) => 代码块(单条返回语句可省略 {} 和 return)
let sum = (num1, num2) => num1 + num2; // 简化写法,等同于 return num1 + num2
console.log(sum(5, 6)); // 输出 11
// 多条语句需用 {} 包裹并显式 return
let multiply = (num1, num2) => {
const result = num1 * num2;
return result;
};
2. 核心特性
- 可调用性:通过
函数名(参数)执行代码(如sum(1,2)); - this 绑定:普通函数的
this指向调用者(如obj.fn()中this指向obj),箭头函数无独立this,继承自父级作用域; - 作为引用类型:可添加属性(如
sum.version = "1.0"),也可作为参数传递(如数组map方法的回调)。
三.存储区别
一、先搞懂两个 “仓库” 的区别
JS 存数据需要两个 “仓库”,分工不同:
- 小仓库(对应 “栈”) :空间小、拿东西快,但只能放 “固定大小、简单的东西”(比如数字 10、字符串 “abc”),用完自动清理。
- 大仓库(对应 “堆”) :空间大、能放 “复杂、大小会变的东西”(比如对象、数组),但拿东西慢,需要 “门牌号” 才能找到。
二、基本类型:小仓库里 “直接存东西”
基本类型(数字、字符串、布尔这些)都很 “简单”,直接存在小仓库里,变量和值是 “绑定死” 的。
1. 存数据:变量和值 “贴一起”
比如写 let a = 10:
- 相当于在小仓库里找个格子,格子上贴个标签 “a”,格子里直接放 “10”—— 以后找 “a”,直接从这个格子里拿 10 就行,不用绕弯。
2. 赋值:复制一份 “值”,各玩各的
再写 let b = a:
- 小仓库会新找一个格子,贴标签 “b”,然后把 “a” 格子里的 10 “复印” 一份,放进 “b” 的格子里。
- 现在 “a” 和 “b” 是两个独立的格子,各存各的 10—— 哪怕后来把 “a” 改成 20(
a = 20),“b” 的格子里还是 10,互不影响。
三、引用类型:大仓库存 “东西”,小仓库存 “门牌号”
引用类型(对象、数组、函数这些)都很 “复杂”(比如对象能加属性、数组能加元素),得存在大仓库里,小仓库只存 “大仓库的门牌号”。
1. 存数据:变量只记 “门牌号”
比如写 let obj1 = {}(空对象):
- 先在大仓库里找个房间,把空对象
{}放进这个房间,给房间编个门牌号(比如 “1001”); - 然后在小仓库找个格子,贴标签 “obj1”,格子里不存对象,只存门牌号 “1001”—— 以后用 “obj1”,得先看小仓库的门牌号,再去大仓库 “1001” 房间拿对象。
2. 赋值:复制 “门牌号”,共享一个 “房间”
再写 let obj2 = obj1:
- 小仓库新找个格子贴 “obj2”,然后把 “obj1” 格子里的门牌号 “1001” 复印一份,放进 “obj2” 的格子里;
- 现在 “obj1” 和 “obj2” 的小仓库格子里,都是 “1001” 这个门牌号 —— 它们指向大仓库同一个房间!
- 所以你给
obj1加属性(obj1.name = "张三"),再看obj2.name也是 “张三”—— 因为改的是大仓库 “1001” 房间里的对象,两个变量共用这个对象。
四、一句话总结:区别就在 “赋值后改数据会不会影响对方”
- 基本类型:赋值是 “复制值”,改一个不影响另一个(比如 a 和 b 各存 10,a 改 20,b 还是 10);
- 引用类型:赋值是 “复制门牌号”,改一个会影响另一个(比如 obj1 和 obj2 都指着 “1001” 房间,改房间里的东西,两个都能看到)。