持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 6 天,点击查看活动详情
ECMAScript 有 8 种基本的数据类型( 7 种原始类型和 1 种引用类型)。
- 原始类型:
Number、BigInt、String、Boolean、Null、Undefined、Symbol - 引用类型:
Object
不同的数据存放在不同的空间中:
- 栈空间:JavaScript 对于基本数据类型内存的分配会在执行时直接在栈空间进行分配;
- 堆空间:JavaScript 对于复杂数据类型内存的分配会在堆内存中开辟一块空间,并且将这块空间的指针(地址)返回给变量引用(存放在栈空间)。
这么做的原因是 JS 引擎需要用栈来维护程序执行期间上下文的状态,如果栈空间分配的内存过大,那么会影响到上下文切换的效率,从而导致代码执行速度过慢。
因此,栈空间不会设置太大,主要用来存放基本数据类型(占用空间小),而像复杂数据类型,因为他们的占用空间一般较大,因此会被存放在堆空间中。
typeof
用来返回操作数类型的字符串。语法:
typeof operand
// or
typeof (operand)
但是由于 JavaScript 设计的缺陷,typeof 基本上不能得到想要的结果。它只有一个实际应用场景:检测一个对象是否已经定义或者是否已经赋值。
特点:
- 对于基本类型,除
null外,均可以返回正确结果; - 对于引用类型,除
function外,一律返回"object"; - 对于
null,返回"object"类型; - 对于
function,返回"function"。
const fn = function() {};
typeof null // "object",得不到想要的值
typeof fn // "function"
总之,数组、对象、null 都会返回 object,其他都能判断正确。
由于 JavaScript 第一个版本中,所有值都存在 32 位单元中,每个单元包含一个小的类型标签以及存储的真实值,object 的类型标签是
000,而 null 的类型标签也是000,因此使用 typeof 判断 null 会被判定为 object。
instanceof
用来检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,返回布尔值。语法:
object instanceof constructor
特点:
- 对于字面量声明的
Number、BigInt、String、Boolean、Symbol都会返回false; - 只能正确判断引用数据类型;
- 只要在当前实例的原型链上,检测结果均为
true;
原理:遍历实例对象的原型链 __proto__,直到找到构造函数的 prototype 属性。
不同环境对
__proto__的实现不同,而且 Web 标准已经删除该特性,这里只是为了表示方便,真实开发中需要使用Object.getPrototypeOf()获取原型对象。
function myInstanceOf(left, right) {
// 在 ES5 中,如果 Object.getPrototypeOf 参数不是一个对象类型
// 将抛出一个TypeError异常。在 ES2015 中,参数会被强制转换为一个 Object。
// 因此需提前判断是否为引用类型
if (typeof left !== "object" && typeof left !== "function") return false
// 获取实例对象的原型
let proto = Object.getPrototypeOf(left)
while (true) {
if (proto === null) return false
if (proto === right.prototype) return true
proto = Object.getProtypeOf(proto)
}
}
准确判断数据类型
如果只需要准确判断六种基本数据类型,同时又能够准确区分数据类型是 null、array、还是 object 就足够的话,那么我们可以这样实现:
const superTypeof = (val) => {
let res = typeof val;
if (res === "object") {
if (val === null) {
res = "null";
} else if (Array.isArray(val)) {
res = "array";
}
}
return res;
};
Object.prototype.toString.call(obj)
当
Array.isArray()不可用时,MDN 做了如下的补丁,因此说明 MDN 推荐使用Object.prototype.toString.call(obj)检测数据类型。
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[Object Array]';
}
}
如果需要判断所有类型,那么可以调用对象原型中的 toString() 方法,Object.prototype.toString.call(obj):
function _getClass (obj) {
if (obj === null) return "null";
return Object.prototype.toString.call(obj).slice(8, -1);
}