Skip to content

数据类型

JavaScript是一种弱类型语言,变量的类型可以在运行时动态改变。JavaScript的数据类型分为两大类:基本数据类型和引用数据类型。本文将详细介绍JavaScript的数据类型及其特性。

数据类型分类

基本数据类型

基本数据类型(Primitive Types)是直接存储在栈中的数据,它们的值是不可变的。JavaScript有7种基本数据类型:

  1. String:字符串类型
  2. Number:数字类型
  3. Boolean:布尔类型
  4. Null:空值类型
  5. Undefined:未定义类型
  6. Symbol:符号类型(ES6+)
  7. BigInt:大整数类型(ES10+)

引用数据类型

引用数据类型(Reference Types)是存储在堆中的对象,栈中存储的是指向堆中对象的引用地址。JavaScript的引用数据类型包括:

  1. Object:对象类型
  2. Array:数组类型
  3. Function:函数类型
  4. Date:日期类型
  5. RegExp:正则表达式类型
  6. Map:映射类型(ES6+)
  7. Set:集合类型(ES6+)
  8. WeakMap:弱映射类型(ES6+)
  9. WeakSet:弱集合类型(ES6+)

基本数据类型

1. String

字符串是由零个或多个字符组成的序列,用单引号(')、双引号(")或反引号(`)包裹。

使用方式

javascript
// 单引号
const str1 = 'Hello';

// 双引号
const str2 = "World";

// 反引号(模板字符串)
const str3 = `Hello ${str2}`; // Hello World

特点

  • 字符串是不可变的,一旦创建就不能修改。
  • 可以使用字符串的方法,但这些方法会返回新的字符串,不会修改原字符串。
  • 模板字符串(Template Literals)使用反引号包裹,可以嵌入表达式。

2. Number

数字类型包括整数和浮点数,JavaScript使用IEEE 754标准表示数字。

使用方式

javascript
// 整数
const num1 = 123;

// 浮点数
const num2 = 123.456;

// 科学记数法
const num3 = 1.23e2; // 123

// 八进制(ES6+)
const num4 = 0o123; // 83

// 十六进制
const num5 = 0x123; // 291

// 二进制(ES6+)
const num6 = 0b1010; // 10

特殊值

  • Infinity:正无穷大
  • -Infinity:负无穷大
  • NaN:非数字(Not a Number)
javascript
const positiveInfinity = Infinity;
const negativeInfinity = -Infinity;
const notANumber = NaN;

console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
console.log('abc' - 1); // NaN

特点

  • JavaScript的数字精度有限,对于非常大或非常小的数字可能会有精度问题。
  • NaN不等于任何值,包括它自己。

3. Boolean

布尔类型只有两个值:truefalse

使用方式

javascript
const isTrue = true;
const isFalse = false;

转换规则: 在条件判断中,以下值会被转换为false(falsy值):

  • false
  • 0
  • -0
  • 0n(BigInt零)
  • ''(空字符串)
  • null
  • undefined
  • NaN

其他值都会被转换为true(truthy值)。

4. Null

null表示一个空值,它是一个特殊的对象类型值。

使用方式

javascript
const emptyValue = null;

特点

  • null表示一个有意的空值。
  • typeof null返回'object',这是JavaScript的一个历史遗留问题。

5. Undefined

undefined表示一个未定义的值,当变量声明但未赋值时,其值为undefined

使用方式

javascript
let unassignedVariable;
console.log(unassignedVariable); // undefined

function foo() {}
console.log(foo()); // undefined

特点

  • undefined表示一个变量未被初始化。
  • typeof undefined返回'undefined'

6. Symbol

Symbol是ES6引入的一种新的基本数据类型,用于创建唯一的标识符。

使用方式

javascript
// 创建Symbol
const sym1 = Symbol();
const sym2 = Symbol('description');

// Symbol是唯一的
const sym3 = Symbol('key');
const sym4 = Symbol('key');
console.log(sym3 === sym4); // false

// 用作对象属性
const obj = {
  [sym1]: 'value1',
  [sym2]: 'value2'
};

特点

  • 每个Symbol值都是唯一的,即使它们的描述相同。
  • Symbol值可以用作对象的属性名,这样可以创建私有属性。
  • Symbol值不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()返回。

7. BigInt

BigInt是ES10引入的一种新的基本数据类型,用于表示任意精度的整数。

使用方式

javascript
// 创建BigInt
const bigInt1 = 123n;
const bigInt2 = BigInt(123);

// 大整数
const bigInt3 = 9007199254740991n; // 2^53 - 1
const bigInt4 = 9007199254740992n; // 超出Number范围

// BigInt运算
const sum = bigInt1 + bigInt2; // 246n
const product = bigInt1 * bigInt2; // 15129n

特点

  • BigInt可以表示任意精度的整数,不受Number类型的范围限制。
  • BigInt使用n后缀或BigInt()构造函数创建。
  • BigInt不能与Number类型直接运算,需要转换类型。

引用数据类型

1. Object

对象是JavaScript中最基本的引用数据类型,它是键值对的集合。

使用方式

javascript
// 对象字面量
const obj1 = {
  name: 'John',
  age: 30,
  sayHello: function() {
    console.log(`Hello, ${this.name}!`);
  }
};

// 构造函数
const obj2 = new Object();
obj2.name = 'Jane';
obj2.age = 25;

// Object.create
const obj3 = Object.create(obj1);
console.log(obj3.name); // John(继承自obj1)

特点

  • 对象是可变的,可以添加、修改和删除属性。
  • 对象是通过引用传递的,不是通过值传递的。
  • 对象可以继承其他对象的属性和方法。

2. Array

数组是一种特殊的对象,用于存储有序的元素集合。

使用方式

javascript
// 数组字面量
const arr1 = [1, 2, 3, 4, 5];

// 构造函数
const arr2 = new Array(5); // 创建长度为5的空数组
const arr3 = new Array(1, 2, 3); // [1, 2, 3]

// 数组方法
arr1.push(6); // 添加元素
arr1.pop(); // 移除最后一个元素
arr1.map(item => item * 2); // 映射数组

特点

  • 数组的长度是可变的,可以动态添加和删除元素。
  • 数组的索引从0开始。
  • 数组继承自Object,具有对象的所有特性。
  • 数组有许多内置方法,如pushpopshiftunshiftmapfilterreduce等。

3. Function

函数是一种特殊的对象,用于封装可重用的代码块。

使用方式

javascript
// 函数声明
function add(a, b) {
  return a + b;
}

// 函数表达式
const subtract = function(a, b) {
  return a - b;
};

// 箭头函数(ES6+)
const multiply = (a, b) => a * b;

// 构造函数
const divide = new Function('a', 'b', 'return a / b');

特点

  • 函数是一等公民,可以作为参数传递,可以作为返回值,可以存储在变量中。
  • 函数有自己的作用域。
  • 函数可以访问外部作用域的变量(闭包)。
  • 箭头函数没有自己的thisargumentsnew.target

4. Date

日期对象用于处理日期和时间。

使用方式

javascript
// 当前日期和时间
const now = new Date();

// 指定日期和时间
const specificDate = new Date('2023-12-25');
const specificDateTime = new Date(2023, 11, 25, 10, 30, 0);

// 日期方法
console.log(now.getFullYear()); // 年份
console.log(now.getMonth()); // 月份(0-11)
console.log(now.getDate()); // 日期(1-31)
console.log(now.getHours()); // 小时(0-23)
console.log(now.getMinutes()); // 分钟(0-59)
console.log(now.getSeconds()); // 秒(0-59)

特点

  • Date对象基于时间戳,即自1970年1月1日00:00:00 UTC以来的毫秒数。
  • Date对象的月份从0开始(0表示1月,11表示12月)。
  • Date对象有许多方法用于获取和设置日期和时间的各个部分。

5. RegExp

正则表达式对象用于匹配字符串中的模式。

使用方式

javascript
// 正则表达式字面量
const regex1 = /pattern/flags;
const regex2 = /\d+/g; // 匹配所有数字

// 构造函数
const regex3 = new RegExp('pattern', 'flags');
const regex4 = new RegExp('\\d+', 'g'); // 匹配所有数字

// 正则表达式方法
const str = 'Hello 123 World 456';
console.log(str.match(regex2)); // ['123', '456']
console.log(regex2.test(str)); // true

标志

  • g:全局匹配
  • i:忽略大小写
  • m:多行匹配
  • s:允许.匹配换行符
  • u:使用Unicode模式
  • y:粘性匹配

特点

  • 正则表达式用于字符串的搜索、替换和验证。
  • 正则表达式有自己的语法规则。
  • 正则表达式对象有许多方法,如testexec等。

6. Map

Map是ES6引入的一种新的数据结构,用于存储键值对,其中键可以是任意类型。

使用方式

javascript
// 创建Map
const map = new Map();

// 添加元素
map.set('name', 'John');
map.set(1, 'one');
map.set(true, 'boolean');

// 获取元素
console.log(map.get('name')); // John

// 检查元素是否存在
console.log(map.has('name')); // true

// 删除元素
map.delete('name');

// 清空Map
map.clear();

// 遍历Map
map.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});

for (const [key, value] of map) {
  console.log(`${key}: ${value}`);
}

特点

  • Map的键可以是任意类型,包括对象、数组、函数等。
  • Map的键是唯一的。
  • Map会记住键的插入顺序。
  • Map的大小可以通过size属性获取。

7. Set

Set是ES6引入的一种新的数据结构,用于存储唯一的值。

使用方式

javascript
// 创建Set
const set = new Set();

// 添加元素
set.add(1);
set.add('hello');
set.add(true);
set.add(1); // 重复元素,不会被添加

// 检查元素是否存在
console.log(set.has(1)); // true

// 删除元素
set.delete(1);

// 清空Set
set.clear();

// 遍历Set
set.forEach(value => {
  console.log(value);
});

for (const value of set) {
  console.log(value);
}

特点

  • Set中的值是唯一的,不会有重复值。
  • Set会记住值的插入顺序。
  • Set的大小可以通过size属性获取。

8. WeakMap

WeakMap是ES6引入的一种新的数据结构,用于存储键值对,其中键只能是对象,且键是弱引用的。

使用方式

javascript
// 创建WeakMap
const weakMap = new WeakMap();

// 添加元素
const obj = {};
weakMap.set(obj, 'value');

// 获取元素
console.log(weakMap.get(obj)); // value

// 检查元素是否存在
console.log(weakMap.has(obj)); // true

// 删除元素
weakMap.delete(obj);

特点

  • WeakMap的键只能是对象。
  • WeakMap的键是弱引用的,即如果没有其他引用指向这个对象,那么这个对象会被垃圾回收。
  • WeakMap没有size属性,也没有clear()方法。
  • WeakMap不能被遍历。

9. WeakSet

WeakSet是ES6引入的一种新的数据结构,用于存储对象,其中对象是弱引用的。

使用方式

javascript
// 创建WeakSet
const weakSet = new WeakSet();

// 添加元素
const obj1 = {};
const obj2 = {};
weakSet.add(obj1);
weakSet.add(obj2);

// 检查元素是否存在
console.log(weakSet.has(obj1)); // true

// 删除元素
weakSet.delete(obj1);

特点

  • WeakSet只能存储对象。
  • WeakSet中的对象是弱引用的,即如果没有其他引用指向这个对象,那么这个对象会被垃圾回收。
  • WeakSet没有size属性,也没有clear()方法。
  • WeakSet不能被遍历。

数据类型检测

1. typeof 操作符

typeof操作符用于检测变量的类型,返回一个字符串。

使用方式

javascript
console.log(typeof 'hello'); // string
console.log(typeof 123); // number
console.log(typeof true); // boolean
console.log(typeof null); // object(历史遗留问题)
console.log(typeof undefined); // undefined
console.log(typeof Symbol()); // symbol
console.log(typeof 123n); // bigint
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof function() {}); // function
console.log(typeof new Date()); // object
console.log(typeof new RegExp()); // object

特点

  • typeof对于基本数据类型(除了null)的检测是准确的。
  • typeof null返回'object',这是JavaScript的一个历史遗留问题。
  • typeof对于引用数据类型(除了function)的检测都返回'object',无法区分具体的对象类型。

2. instanceof 操作符

instanceof操作符用于检测对象是否是某个构造函数的实例,返回一个布尔值。

使用方式

javascript
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true
console.log({} instanceof Object); // true
console.log(function() {} instanceof Function); // true
console.log(new Date() instanceof Date); // true
console.log(new RegExp() instanceof RegExp); // true

特点

  • instanceof用于检测引用数据类型的具体类型。
  • instanceof基于原型链进行检测,因此一个对象可能是多个构造函数的实例。
  • instanceof不能用于检测基本数据类型。

3. Object.prototype.toString.call()

Object.prototype.toString.call()方法用于检测任意值的类型,返回一个格式为'[object Type]'的字符串。

使用方式

javascript
console.log(Object.prototype.toString.call('hello')); // [object String]
console.log(Object.prototype.toString.call(123)); // [object Number]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(Symbol())); // [object Symbol]
console.log(Object.prototype.toString.call(123n)); // [object BigInt]
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(function() {})); // [object Function]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(new RegExp())); // [object RegExp]
console.log(Object.prototype.toString.call(new Map())); // [object Map]
console.log(Object.prototype.toString.call(new Set())); // [object Set]

特点

  • Object.prototype.toString.call()是最准确的类型检测方法,它可以检测所有数据类型,包括nullundefined
  • 它返回的字符串格式为'[object Type]',其中Type是数据类型的名称。

4. Array.isArray()

Array.isArray()方法用于检测值是否是数组,返回一个布尔值。

使用方式

javascript
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray('hello')); // false

特点

  • Array.isArray()是检测数组的专门方法,比instanceof更准确,因为它可以跨iframe检测。

5. 其他检测方法

  • Number.isNaN():检测值是否是NaN
  • Number.isFinite():检测值是否是有限的数字
  • Number.isInteger():检测值是否是整数
  • isNaN():检测值是否是NaN(会进行类型转换)
  • isFinite():检测值是否是有限的数字(会进行类型转换)

数据类型转换

1. 显式类型转换

1.1 转换为字符串

  • String()函数
  • toString()方法
javascript
console.log(String(123)); // "123"
console.log(String(true)); // "true"
console.log(String(null)); // "null"
console.log(String(undefined)); // "undefined"

console.log((123).toString()); // "123"
console.log(true.toString()); // "true"
// null和undefined没有toString()方法

1.2 转换为数字

  • Number()函数
  • parseInt()函数
  • parseFloat()函数
  • +一元运算符
javascript
console.log(Number('123')); // 123
console.log(Number('123.456')); // 123.456
console.log(Number('')); // 0
console.log(Number('true')); // 1
console.log(Number('false')); // 0
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN

console.log(parseInt('123')); // 123
console.log(parseInt('123.456')); // 123
console.log(parseInt('123abc')); // 123
console.log(parseInt('abc123')); // NaN

console.log(parseFloat('123')); // 123
console.log(parseFloat('123.456')); // 123.456
console.log(parseFloat('123.456abc')); // 123.456

console.log(+'123'); // 123
console.log(+'123.456'); // 123.456

1.3 转换为布尔值

  • Boolean()函数
  • !!双重否定运算符
javascript
console.log(Boolean('hello')); // true
console.log(Boolean('')); // false
console.log(Boolean(123)); // true
console.log(Boolean(0)); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false

console.log(!!'hello'); // true
console.log(!!''); // false
console.log(!!123); // true
console.log(!!0); // false

2. 隐式类型转换

JavaScript在某些操作中会自动进行类型转换,这称为隐式类型转换。

2.1 字符串拼接

当使用+运算符连接字符串和其他类型的值时,其他类型的值会被转换为字符串。

javascript
console.log('hello' + 123); // "hello123"
console.log('hello' + true); // "hellotrue"
console.log('hello' + null); // "hellonull"
console.log('hello' + undefined); // "helloundefined"

2.2 算术运算

当使用算术运算符(除了+)操作非数字类型的值时,非数字类型的值会被转换为数字。

javascript
console.log(123 - '45'); // 78
console.log(123 * '45'); // 5535
console.log(123 / '45'); // 2.7333333333333334
console.log(123 % '45'); // 33
console.log('123' - 0); // 123
console.log(true - 0); // 1
console.log(false - 0); // 0
console.log(null - 0); // 0
console.log(undefined - 0); // NaN

2.3 比较运算

当使用比较运算符比较不同类型的值时,会进行隐式类型转换。

javascript
console.log(123 == '123'); // true(会进行类型转换)
console.log(123 === '123'); // false(不会进行类型转换)
console.log(0 == false); // true
console.log(0 === false); // false
console.log(null == undefined); // true
console.log(null === undefined); // false

2.4 逻辑运算

当使用逻辑运算符时,会进行隐式类型转换为布尔值。

javascript
console.log(!0); // true
console.log(!''); // true
console.log(!null); // true
console.log(!undefined); // true
console.log(!NaN); // true
console.log(!123); // false
console.log(!'hello'); // false

console.log(0 || 'default'); // "default"
console.log('hello' || 'default'); // "hello"
console.log(0 && 'value'); // 0
console.log('hello' && 'value'); // "value"

面试常见问题

1. JavaScript的数据类型有哪些?

JavaScript的数据类型分为两大类:

  • 基本数据类型:String、Number、Boolean、Null、Undefined、Symbol(ES6+)、BigInt(ES10+)
  • 引用数据类型:Object、Array、Function、Date、RegExp、Map(ES6+)、Set(ES6+)、WeakMap(ES6+)、WeakSet(ES6+)

2. 基本数据类型和引用数据类型的区别是什么?

  • 存储位置:基本数据类型存储在栈中,引用数据类型存储在堆中,栈中存储的是指向堆中对象的引用地址。
  • 复制方式:基本数据类型是按值复制的,引用数据类型是按引用复制的。
  • 可变性:基本数据类型是不可变的,引用数据类型是可变的。
  • 比较方式:基本数据类型比较的是值,引用数据类型比较的是引用地址。

3. typeof null为什么返回'object'?

这是JavaScript的一个历史遗留问题。在JavaScript的早期实现中,值是通过32位的字来表示的,其中最低的3位用于表示类型标签。对于对象,类型标签是000。而null被表示为全0,因此它的类型标签也是000,被错误地识别为对象。

4. 如何准确检测一个值的类型?

使用Object.prototype.toString.call()方法,它可以检测任意值的类型,返回一个格式为'[object Type]'的字符串。

5. == 和 === 的区别是什么?

  • ==:宽松相等运算符,会进行隐式类型转换,然后比较值。
  • ===:严格相等运算符,不会进行隐式类型转换,直接比较值和类型。

6. 什么是falsy值?

在条件判断中,以下值会被转换为false

  • false
  • 0
  • -0
  • 0n(BigInt零)
  • ''(空字符串)
  • null
  • undefined
  • NaN

7. 如何将字符串转换为数字?

  • Number()函数:转换整个字符串为数字。
  • parseInt()函数:解析字符串的整数部分。
  • parseFloat()函数:解析字符串的浮点数部分。
  • +一元运算符:将字符串转换为数字。

8. 如何将数字转换为字符串?

  • String()函数:将数字转换为字符串。
  • toString()方法:将数字转换为字符串,可以指定进制。
  • '' + 数字:使用字符串拼接进行隐式类型转换。

9. Symbol的特点是什么?

  • 每个Symbol值都是唯一的,即使它们的描述相同。
  • Symbol值可以用作对象的属性名,这样可以创建私有属性。
  • Symbol值不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()返回。

10. BigInt的特点是什么?

  • BigInt可以表示任意精度的整数,不受Number类型的范围限制。
  • BigInt使用n后缀或BigInt()构造函数创建。
  • BigInt不能与Number类型直接运算,需要转换类型。

11. Map和Object的区别是什么?

  • 键的类型:Map的键可以是任意类型,Object的键只能是字符串或Symbol。
  • 键的顺序:Map会记住键的插入顺序,Object在ES6之前不保证键的顺序。
  • 大小:Map的大小可以通过size属性获取,Object的大小需要手动计算。
  • 遍历:Map可以通过forEachfor...of等方法遍历,Object需要通过for...inObject.keys()等方法遍历。
  • 性能:对于频繁添加和删除键值对的场景,Map的性能更好。

12. Set和Array的区别是什么?

  • 唯一性:Set中的值是唯一的,Array中的值可以重复。
  • 顺序:Set会记住值的插入顺序,Array也会记住值的插入顺序。
  • 大小:Set的大小可以通过size属性获取,Array的大小可以通过length属性获取。
  • 方法:Set有自己的方法,如adddeletehas等,Array有自己的方法,如pushpopshift等。
  • 遍历:Set可以通过forEachfor...of等方法遍历,Array也可以通过这些方法遍历。

总结

JavaScript的数据类型是JavaScript的基础,理解数据类型的特性和转换规则对于编写高质量的JavaScript代码非常重要。

  • 基本数据类型包括String、Number、Boolean、Null、Undefined、Symbol和BigInt,它们存储在栈中,是不可变的。
  • 引用数据类型包括Object、Array、Function、Date、RegExp、Map、Set、WeakMap和WeakSet,它们存储在堆中,是可变的。
  • 数据类型检测方法包括typeof、instanceof、Object.prototype.toString.call()、Array.isArray()等。
  • 数据类型转换包括显式类型转换和隐式类型转换,了解这些转换规则可以避免很多常见的错误。

通过掌握JavaScript的数据类型,你可以更好地理解JavaScript的行为,编写更加健壮和高效的代码。

好好学习,天天向上