数据类型概述

原始类型与引用类型

JavaScript的数据类型分为两大类:原始类型(Primitive Types)和引用类型(Reference Types)。

原始类型

原始类型存储的是实际的值,共有7种:

// 1. Number - 数字
let age = 25;
let price = 99.99;
let temperature = -10;

// 2. String - 字符串
let name = "东巴文";
let greeting = '你好';
let template = `欢迎`;

// 3. Boolean - 布尔值
let isActive = true;
let isLoggedIn = false;

// 4. Undefined - 未定义
let notDefined;
console.log(notDefined);  // undefined

// 5. Null - 空值
let empty = null;

// 6. Symbol - 符号(ES6)
let id = Symbol("unique");

// 7. BigInt - 大整数(ES2020)
let bigNumber = 9007199254740991n;

引用类型

引用类型存储的是指向内存位置的引用:

// Object - 对象
let user = {
    name: "东巴文",
    age: 1
};

// Array - 数组
let numbers = [1, 2, 3, 4, 5];

// Function - 函数
function greet() {
    return "Hello";
}

// Date - 日期
let now = new Date();

// RegExp - 正则表达式
let pattern = /\d+/;

// Map、Set 等
let map = new Map();
let set = new Set();

两者的区别

特性 原始类型 引用类型 东巴文比喻
存储方式 存储实际值 存储引用地址 原始类型是值,引用类型是地址
存储位置 栈内存 堆内存 栈快,堆大
复制行为 复制值 复制引用 原始类型独立,引用类型共享
比较 比较值 比较引用地址 原始类型看内容,引用类型看地址
可变性 不可变 可变 原始类型不能改,引用类型可以改

值复制与引用复制

// 原始类型 - 值复制
let a = 10;
let b = a;  // 复制值
b = 20;
console.log(a);  // 10(a不受影响)

// 引用类型 - 引用复制
let obj1 = { name: "东巴文" };
let obj2 = obj1;  // 复制引用
obj2.name = "新名字";
console.log(obj1.name);  // "新名字"(obj1也变了)

东巴文图解

原始类型:
变量名    栈内存
a    →   10
b    →   20(复制后独立)

引用类型:
变量名    栈内存              堆内存
obj1 →   引用地址 ──────→   { name: "新名字" }
obj2 →   引用地址 ──────→   (指向同一对象)

值比较与引用比较

// 原始类型 - 比较值
console.log(10 === 10);           // true
console.log("东巴文" === "东巴文"); // true

// 引用类型 - 比较引用地址
let obj1 = { name: "东巴文" };
let obj2 = { name: "东巴文" };
console.log(obj1 === obj2);  // false(不同对象)

let obj3 = obj1;
console.log(obj1 === obj3);  // true(同一引用)

typeof操作符

typeof操作符用于检测数据类型,返回一个字符串。

基本用法

typeof 42;              // "number"
typeof 3.14;            // "number"
typeof "东巴文";         // "string"
typeof true;            // "boolean"
typeof undefined;       // "undefined"
typeof null;            // "object"(历史遗留问题)
typeof Symbol();        // "symbol"
typeof 10n;             // "bigint"

typeof {};              // "object"
typeof [];              // "object"
typeof function(){};    // "function"
typeof null;            // "object"

typeof返回值总结

类型 返回值 东巴文备注
Number "number" 包括NaN和Infinity
String "string" -
Boolean "boolean" -
Undefined "undefined" -
Null "object" ⚠️ 历史遗留问题
Symbol "symbol" ES6新增
BigInt "bigint" ES2020新增
Object "object" 包括数组、正则等
Function "function" 函数是特殊的对象

typeof的注意事项

// 1. null的特殊情况
typeof null;  // "object"(这是JavaScript的bug)

// 正确检测null
function isNull(value) {
    return value === null;
}

// 2. 数组的检测
typeof [];  // "object"

// 正确检测数组
Array.isArray([]);  // true
[] instanceof Array;  // true

// 3. NaN的检测
typeof NaN;  // "number"

// 正确检测NaN
Number.isNaN(NaN);  // true

// 4. 未声明变量的检测
// console.log(notDefined);  // ReferenceError
typeof notDefined;  // "undefined"(不会报错)

安全的类型检测

function getType(value) {
    if (value === null) return "null";
    if (Array.isArray(value)) return "array";
    if (value instanceof Date) return "date";
    if (value instanceof RegExp) return "regexp";
    return typeof value;
}

console.log(getType(null));      // "null"
console.log(getType([]));        // "array"
console.log(getType(new Date())); // "date"
console.log(getType(42));        // "number"

类型判断方法

typeof方法

适用于原始类型(除null外):

function isNumber(value) {
    return typeof value === "number" && !Number.isNaN(value);
}

function isString(value) {
    return typeof value === "string";
}

function isBoolean(value) {
    return typeof value === "boolean";
}

function isUndefined(value) {
    return typeof value === "undefined";
}

function isSymbol(value) {
    return typeof value === "symbol";
}

function isBigInt(value) {
    return typeof value === "bigint";
}

instanceof方法

适用于引用类型,检测原型链:

function isObject(value) {
    return value !== null && typeof value === "object";
}

function isArray(value) {
    return Array.isArray(value);
}

function isFunction(value) {
    return typeof value === "function";
}

function isDate(value) {
    return value instanceof Date;
}

function isRegExp(value) {
    return value instanceof RegExp;
}

function isMap(value) {
    return value instanceof Map;
}

function isSet(value) {
    return value instanceof Set;
}

Object.prototype.toString方法

最准确的类型检测方法:

function getExactType(value) {
    return Object.prototype.toString.call(value).slice(8, -1);
}

console.log(getExactType(null));           // "Null"
console.log(getExactType(undefined));      // "Undefined"
console.log(getExactType(42));             // "Number"
console.log(getExactType("东巴文"));        // "String"
console.log(getExactType(true));           // "Boolean"
console.log(getExactType([]));             // "Array"
console.log(getExactType({}));             // "Object"
console.log(getExactType(function(){}));   // "Function"
console.log(getExactType(new Date()));     // "Date"
console.log(getExactType(/regex/));        // "RegExp"
console.log(getExactType(new Map()));      // "Map"
console.log(getExactType(new Set()));      // "Set"
console.log(getExactType(Symbol()));       // "Symbol"
console.log(getExactType(10n));            // "BigInt"

综合类型检测函数

function typeOf(value) {
    const type = Object.prototype.toString.call(value).slice(8, -1);
    
    const typeMap = {
        'Number': 'number',
        'String': 'string',
        'Boolean': 'boolean',
        'Undefined': 'undefined',
        'Null': 'null',
        'Symbol': 'symbol',
        'BigInt': 'bigint',
        'Object': 'object',
        'Array': 'array',
        'Function': 'function',
        'Date': 'date',
        'RegExp': 'regexp',
        'Map': 'map',
        'Set': 'set',
        'WeakMap': 'weakmap',
        'WeakSet': 'weakset',
        'Promise': 'promise',
        'Error': 'error'
    };
    
    return typeMap[type] ? typeMap[type] : type.toLowerCase();
}

console.log(typeOf(null));      // "null"
console.log(typeOf([]));        // "array"
console.log(typeOf({}));        // "object"
console.log(typeOf(42));        // "number"
console.log(typeOf(new Map())); // "map"

类型转换概述

JavaScript是一种弱类型语言,支持隐式类型转换,也支持显式类型转换。

隐式类型转换

JavaScript在某些操作中会自动进行类型转换:

// 字符串拼接
console.log(1 + "2");        // "12"
console.log("东巴文" + 2024); // "东巴文2024"

// 算术运算
console.log("5" - 3);        // 2
console.log("5" * "2");      // 10
console.log("10" / 2);       // 5

// 比较运算
console.log(5 == "5");       // true(隐式转换)
console.log(5 === "5");      // false(严格相等)

// 布尔转换
if ("非空字符串") {
    console.log("执行");     // 执行
}

// 逻辑运算
console.log(1 || 2);         // 1
console.log(0 && 1);         // 0

显式类型转换

手动进行类型转换更加清晰可控:

// 转换为字符串
String(123);           // "123"
(123).toString();      // "123"
123 + "";              // "123"

// 转换为数字
Number("123");         // 123
parseInt("123");       // 123
parseFloat("3.14");    // 3.14
+"123";                // 123

// 转换为布尔值
Boolean(1);            // true
!!1;                   // true
Boolean("");           // false
!!"";                  // false

// 转换为对象
Object(123);           // Number {123}
Object("东巴文");       // String {"东巴文"}

转换规则速查表

转换为字符串

原始值 转换结果 东巴文说明
undefined "undefined" -
null "null" -
true "true" -
false "false" -
123 "123" -
NaN "NaN" -
Infinity "Infinity" -
[] "" 空数组
[1, 2] "1,2" 数组元素用逗号连接
{} "[object Object]" 对象

转换为数字

原始值 转换结果 东巴文说明
undefined NaN -
null 0 -
true 1 -
false 0 -
"123" 123 纯数字字符串
"3.14" 3.14 浮点数
"" 0 空字符串
"东巴文" NaN 非数字字符串
[] 0 空数组
[5] 5 单元素数组
[1, 2] NaN 多元素数组
{} NaN 对象

转换为布尔值

原始值 转换结果 东巴文说明
undefined false 假值
null false 假值
0 false 假值
-0 false 假值
NaN false 假值
"" false 假值(空字符串)
false false 假值
其他 true 真值

假值(Falsy Values)

JavaScript中只有以下值会被转换为false:

const falsyValues = [
    false,
    0,
    -0,
    0n,
    "",      // 空字符串
    null,
    undefined,
    NaN
];

falsyValues.forEach(value => {
    console.log(Boolean(value));  // 全部输出 false
});

// 注意:这些值是真值(Truthy)
console.log(Boolean([]));        // true(空数组)
console.log(Boolean({}));        // true(空对象)
console.log(Boolean(" "));       // true(空格字符串)
console.log(Boolean("false"));   // true(字符串"false")

东巴文提醒:空数组[]和空对象{}是真值,这在条件判断时要特别注意。

类型转换的陷阱

字符串拼接陷阱

console.log(1 + 2 + "3");    // "33"
console.log("1" + 2 + 3);    // "123"
console.log(1 + "2" + 3);    // "123"

// 解析:
// 1 + 2 = 3, 然后 3 + "3" = "33"
// "1" + 2 = "12", 然后 "12" + 3 = "123"

比较运算陷阱

console.log([] == false);     // true
console.log([] == ![]);       // true
console.log({} == !{});       // false

// 解析:
// [] 转换为 "" 再转换为 0
// false 转换为 0
// 所以 [] == false 为 true

// 推荐:使用严格相等 ===
console.log([] === false);    // false
console.log([] === ![]);      // false

对象转原始值

const obj = {
    valueOf() {
        return 42;
    },
    toString() {
        return "东巴文";
    }
};

console.log(obj + 8);         // 50(调用valueOf)
console.log(String(obj));     // "东巴文"(调用toString)

// 没有valueOf时
const obj2 = {
    toString() {
        return "东巴文";
    }
};

console.log(obj2 + "你好");    // "东巴文你好"

东巴文建议:了解类型转换规则,但尽量使用显式转换,避免隐式转换带来的意外。

下一步

了解了数据类型概述后,让我们深入学习:

  1. 原始类型 - 深入学习原始类型
  2. 引用类型 - 深入学习引用类型
  3. 类型转换 - 深入学习类型转换

东巴文(db-w.cn) - 让编程学习更简单

🔍 东巴文寄语:理解数据类型是JavaScript编程的基础。掌握原始类型和引用类型的区别,能帮助你避免很多常见的错误。在 db-w.cn,我们帮你建立扎实的知识体系!