
数据类型 
温馨提示
阅读《JavaScript 高级程序设计(第 4 版)》和各个大佬的文章所归纳的总结,如有异议按你的理解为主
JavaScript 中的数据类型分为基本数据类型和引用数据类型
基本类型 
注: 基本数据类型也可以叫原始数据类型
在 ES2020 标准下的 JavaScript 一共有以下 7 种基本类型
基本类型总结
- 基本类型仅保存原始值,不存在属性和方法
 - 变量中的基本类型存储在 栈内存 中
 - 保存基本类型的变量是 按值 (by value) 访问 的,操作的就是存储在变量中的实际值
 - 复制基本类型时会创建该值的第二个副本 (独立使用,互不干扰)
 
为什么原始值不存在属性和方法,但 'hello world'.toString() 可以正确执行
为了方便操作原始值 ECMAScript 提供了 3 种特殊的引用类型:Boolean Number String,每当用到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,在执行完后再销毁这个包装对象
js
// 举个 🌰
const str = 'hello world'
str.toString()
str.length
/**
 * 在执行上面的代码时 `JavaScript` 都会执行以下 3 步
 * 1. 创建一个 String 类型的实例
 * 2. 调用实例上的特定方法或属性
 * 3. 销毁刚刚创建的实例
 */
const str = 'hello world'
new String(str).toString()
new String(str).length引用类型 
在 JavaScript 中除了基本类型,其他的都是引用类型,常见的引用类型如下
Object对象Array数组Function函数Date日期与时间RegExp正则表达式Set类似于数组但成员的值都是唯一的 (ES6 引入)WeakSet(ES6 引入)Map类似于对象也是键值对的集合 (ES6 引入)WeakMap(ES6 引入)
引用类型总结
- 因为 
JavaScript不允许直接访问内存位置(不能直接操作对象所在的内存空间),所以引用类型在 栈内存 中存储的是地址(内存指针),而引用类型中的数据(方法或属性)是存储在 堆内存 中 - 保存引用类型的变量是 按引用 (by reference) 访问 ,实际上操作的是对该对象的引用而非实际的对象本身
 - 复制引用类型时只会复制内存指针
 
栈内存和堆内存
- 栈内存
- 存储的是变量值和引用类型的地址
 - 是连续的内存空间
 
 - 堆内存
- 存储引用数据类型和闭包中的变量
 - 不是连续的内存空间
 
 - 了解更多请点击 JS 中的栈内存和堆内存
 
类型判断 
常见的五种判断方式
typeofinstanceofconstructorArray.isArray()Object.prototype.toString
typeof 
- 除 
null外的基本类型都能准确判断 
js
typeof undefined        // 'undefined'
typeof null             // 'object'
typeof true             // 'boolean'
typeof 'maomao'         // 'string'
typeof 2021             // 'number'
typeof Symbol()         // 'symbol'
typeof BigInt(2021)     // 'bigint'为什么 typeof null === 'object'
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此null 的类型标签是 0,typeof null 也因此返回 "object" —— MDN
- 除 
function外的引用类型均返回object 
js
typeof {}               // 'object'
typeof []               // 'object'
typeof console.log      // 'function'
typeof new Date()       // 'object'
typeof new RegExp()     // 'object'
typeof new Set()        // 'object'
typeof new WeakSet()    // 'object'
typeof new Map()        // 'object'
typeof new WeakMap()    // 'object'instanceof 
instanceof 用于检测构造函数的 prototype 属性是否存在于实例对象的原型链上
js
/** 基本类型 */
true instanceof Boolean       // false
'maomao' instanceof String    // false
1 instanceof Number           // false
/** 引用类型 */
function Person(name) {
  this.name = name
}
const p1 = new Person('maomao')
p1 instanceof Person          // true
p1 instanceof Object          // true
// 修改原型,使 p1 不再是 Person 的实例
Reflect.setPrototypeOf(p1, Array.prototype)
// OR p1.__proto__ = Array.prototype
p1 instanceof Person          // false
p1 instanceof Array           // trueinstanceof 总结
instanceof不能判断基本类型,对于引用类型只能判断原型链上的从属关系instanceof并不完全可靠,因为构造函数的prototype属性可能会被修改- 修改原型的方法 
- 使用 
ES6提供的Reflect.setPrototypeOf()方法 - 借助于非标准的 
__proto__伪属性 
 - 使用 
 
- 修改原型的方法 
 
constructor 
实例对象可以通过 constructor 属性去访问它的构造函数
js
/** 基本类型 */
(true).constructor === Boolean            // true
'maomao'.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   // trueconstructor 总结
constructor可以判断除undefined和null外的所有基本类型和引用类型(undefined和null不存在构造函数)constructor并不完全可靠,因为构造函数的prototype属性可能会被修改,从而造成constructor属性指向不准确
Array.isArray() 
Array.isArray() 用于判断一个值是否是数组 (Array)
js
Array.isArray([])   // true
Array.isArray({})   // falseObject.prototype.toString 
- 每个对象都有一个 
toString()方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用,默认情况下toString()方法被每个Object对象继承。如果此方法在自定义对象中未被覆盖toString()返回"[object type]"其中type是对象的类型 - 为了每个对象都能通过 
Object.prototype.toString()来检测,需要以Function.prototype.call()或者Function.prototype.apply()的形式来调用 
js
const toString = Object.prototype.toString
toString.call(undefined)        // '[object Undefined]'
toString.call(null)             // '[object Null]'
toString.call(true)             // '[object Boolean]'
toString.call('maomao')         // '[object String]'
toString.call(2021)             // '[object Number]'
toString.call(Symbol())         // '[object Symbol]'
toString.call(BigInt(2021))     // '[object BigInt]'
toString.call({})               // '[object Object]'
toString.call([])               // '[object Array]'
toString.call(console.log)      // '[object Function]'
toString.call(new Date())       // '[object Date]'
toString.call(new RegExp())     // '[object RegExp]'
toString.call(new Set())        // '[object Set]'
toString.call(new WeakSet())    // '[object WeakSet]'
toString.call(new Map())        // '[object Map]'
toString.call(new WeakMap())    // '[object WeakMap]'toString 方法的在 ECMAScript 5 下的大致执行过程
- 如果 
this是undefined返回[object Undefined] - 如果 
this是null返回[object Null] - 让 
O成为ToObject(this)的结果 - 让 
class成为O的内部属性[[Class]]的值 - 返回由 
"[object "class"]"三个部分组成的字符串 

