javascript 数据类型

温馨提示阅读

《JavaScript 高级程序设计(第 4 版)》和各个大佬的文章所归纳的总结,如有异议按你的理解为主

JavaScript 中的数据类型分为基本数据类型和引用数据类型

基本类型

注: 基本数据类型也可以叫原始数据类型

ES2020 标准下的 JavaScript 一共有以下 7 种基本类型

  • undefined 未定义
  • null 空指针
  • boolean 布尔值
  • string 字符串
  • number 数值
  • symbol 独一无二的值 (ES6 引入)
  • bigint 大整数 (ES2020 引入)
基本类型总结
  • 基本类型仅保存原始值,不存在属性和方法
  • 基本类型存储在 栈内存
  • 保存基本类型的变量是 按值 (by value) 访问 的,操作的就是存储在变量中的实际值
  • 复制基本类型时会创建该值的第二个副本 (独立使用,互不干扰)
为什么原始值不存在属性和方法,但 'hello world'.toString() 可以正确执行为了方便操作原始值

ECMAScript 提供了 3 种特殊的引用类型:Boolean Number String,每当用到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,在执行完后再销毁这个包装对象

1// 举个 🌰
2const str = 'hello world';
3str.toString();
4str.length;
5
6/**
7 * 在执行上面的代码时 `JavaScript` 都会执行以下 3 步
8 * 1. 创建一个 String 类型的实例
9 * 2. 调用实例上的特定方法或属性
10 * 3. 销毁刚刚创建的实例
11 */
12const str = 'hello world';
13new String(str).toString();
14new String(str).length;

引用类型

JavaScript 中除了基本类型,其他的都是引用类型,常见的引用类型如下

  • Object 对象
  • Array 数组
  • Function 函数
  • Date 日期与时间
  • RegExp 正则表达式
  • Set 类似于数组但成员的值都是唯一的 (ES6 引入)
  • WeakSet (ES6 引入)
  • Map 类似于对象也是键值对的集合 (ES6 引入)
  • WeakMap (ES6 引入)
引用类型总结
  • 因为 JavaScript 不允许直接访问内存位置(不能直接操作对象所在的内存空间),所以引用类型在 栈内存 中存储的是地址(内存指针),而引用类型中的数据(方法或属性)是存储在 堆内存
  • 保存引用类型的变量是 按引用 (by reference) 访问 ,实际上操作的是对该对象的引用而非实际的对象本身
  • 复制引用类型时只会复制内存指针
栈内存和堆内存
  • 栈内存
    • 存储基本数据类型和堆内存地址
    • 是连续的内存空间
  • 堆内存
    • 存储引用数据类型和闭包中的变量
    • 不是连续的内存空间
  • 了解更多请点击 JS 中的栈内存和堆内存

类型判断

常见的五种判断方式

  • typeof
  • instanceof
  • constructor
  • Array.isArray()
  • Object.prototype.toString

typeof

  • null 外的基本类型都能准确判断
1typeof undefined; // 'undefined'
2typeof null; // 'object'
3typeof true; // 'boolean'
4typeof 'h7ml'; // 'string'
5typeof 2021; // 'number'
6typeof Symbol(); // 'symbol'
7typeof BigInt(2021); // 'bigint'
8
9typeof {}; // 'object'
10typeof []; // 'object'
11typeof console.log; // 'function'
12typeof new Date(); // 'object'
13typeof new RegExp(); // 'object'
14typeof new Set(); // 'object'
15typeof new WeakSet(); // 'object'
16typeof new Map(); // 'object'
17typeof new WeakMap(); // 'object'
为什么 typeof null === 'object' 在

JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此null 的类型标签是 0typeof null 也因此返回 "object" —— MDN

  • function 外的引用类型均返回 object
1typeof undefined; // 'undefined'
2typeof null; // 'object'
3typeof true; // 'boolean'
4typeof 'h7ml'; // 'string'
5typeof 2021; // 'number'
6typeof Symbol(); // 'symbol'
7typeof BigInt(2021); // 'bigint'
8
9typeof {}; // 'object'
10typeof []; // 'object'
11typeof console.log; // 'function'
12typeof new Date(); // 'object'
13typeof new RegExp(); // 'object'
14typeof new Set(); // 'object'
15typeof new WeakSet(); // 'object'
16typeof new Map(); // 'object'
17typeof new WeakMap(); // 'object'

instanceof

instanceof 用于检测构造函数的 prototype 属性是否存在于实例对象的原型链上

1/** 基本类型 */
2true instanceof Boolean; // false
3'h7ml' instanceof String; // false
41 instanceof Number; // false
5
6/** 引用类型 */
7function Person(name) {
8  this.name = name;
9}
10
11const p1 = new Person('h7ml');
12
13p1 instanceof Person; // true
14p1 instanceof Object; // true
15
16// 修改原型,使 p1 不再是 Person 的实例
17Reflect.setPrototypeOf(p1, Array.prototype);
18// OR p1.__proto__ = Array.prototype
19
20p1 instanceof Person; // false
21p1 instanceof Array; // true
instanceof 总结
  • instanceof 不能判断基本类型,对于引用类型只能判断原型链上的从属关系
  • instanceof 并不完全可靠,因为构造函数的 prototype 属性可能会被修改

constructor

实例对象可以通过 constructor 属性去访问它的构造函数

/** 基本类型 */ (true).constructor === Boolean // true 'h7ml'.constructor === String // true (2021).constructor === Number // true Symbol().constructor === Symbol // true BigInt(2021).constructor === BigInt // true /** 引用类型 */ ({}).constructor === Object // true ([]).constructor === Array // true function Person(name) { this.name = name } Person.prototype.constructor === Person // true // 修改原型造成 constructor 丢失 Person.prototype = {} Person.prototype.constructor === Object // true
constructor 总结
  • constructor 可以判断除 undefinednull 外的所有基本类型和引用类型(undefinednull 不存在构造函数)
  • constructor 并不完全可靠,因为构造函数的 prototype 属性可能会被修改,从而造成 constructor 属性指向不准确

Array.isArray()

Array.isArray() 用于判断一个值是否是数组 (Array)

1Array.isArray([]); // true
2Array.isArray({}); // false

Object.prototype.toString

  • 每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用,默认情况下 toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖 toString() 返回 "[object type]" 其中 type 是对象的类型
  • 为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用
1const toString = Object.prototype.toString;
2
3toString.call(undefined); // '[object Undefined]'
4toString.call(null); // '[object Null]'
5toString.call(true); // '[object Boolean]'
6toString.call('h7ml'); // '[object String]'
7toString.call(2021); // '[object Number]'
8toString.call(Symbol()); // '[object Symbol]'
9toString.call(BigInt(2021)); // '[object BigInt]'
10
11toString.call({}); // '[object Object]'
12toString.call([]); // '[object Array]'
13toString.call(console.log); // '[object Function]'
14toString.call(new Date()); // '[object Date]'
15toString.call(new RegExp()); // '[object RegExp]'
16toString.call(new Set()); // '[object Set]'
17toString.call(new WeakSet()); // '[object WeakSet]'
18toString.call(new Map()); // '[object Map]'
19toString.call(new WeakMap()); // '[object WeakMap]'

toString 方法的在 ECMAScript 5 下的大致执行过程

  1. 如果 thisundefined 返回 [object Undefined]
  2. 如果 thisnull 返回 [object Null]
  3. O 成为 ToObject(this) 的结果
  4. class 成为 O 的内部属性 [[Class]] 的值
  5. 返回由 "[object " class "]" 三个部分组成的字符串
注意点

不同 ECMAScript 版本对 toString 方法的规范都有所不同

Object.prototype.toString 方法的原理