数据类型

JS 数据类型分为两大类:

1、原始数据类型StringNumberBooleanUndefinedNullSymbol(es6 新增,表示独一无二的值)、Bigint(es10 新增)

2、引用类型Object

其中 Object 中又包含了很多子类型(通过原型链继承),比如 ArrayDateFunctionMathMapSetRegexp 等等,总之除了原始数据类型皆为引用类型。

存储方式

原始数据类型:直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据

引用数据类型:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储指针,指向堆中存的实体的地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

ps:这样设计的原因在于
原始类型因为占据空间是固定的,可以将他们存在较小的内存中-栈中,这样便于迅速查询变量的值。引用数据类型大小不固定且会变化,固然不能直接放在栈中,但是其地址是固定的,可以将地址存在栈中。

类型判断

类型判断有以下几种方式

  • typeof
  • instanceof
  • constructor
  • Object.prototype.toString.call()

typeof

原始类型中除了null会判断成Object,其它类型都可以通过 typeof 来判断
引用类型除了 函数 都会显示 Object

1
2
3
4
5
6
7
8
console.log(typeof 1); // number
console.log(typeof true); // boolean
console.log(typeof "hello"); // string
console.log(typeof undefined); // undefined
console.log(typeof null); // 被 typeof 解释为 object
console.log(typeof {}); // object
console.log(typeof function () {}); // function
console.log(typeof []); // object 数组被解释为 object

instanceof

能正确判断引用类型,不能精准判断原始数据类型
原理:通过递归查找原型链的方式来判断是否为构造函数的实例

1
2
3
4
5
6
console.log({} instanceof Object); // true
console.log(function () {} instanceof Function); // true
console.log(1 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log("hello" instanceof String); // false
console.log(Array.isArray([])); // true

constructor

通过判断 constructor 确定数据类型,不可靠在于,如果创建的对象更改了原型,就不准确了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
console.log((1).constructor === Number); // true
console.log(true.constructor === Boolean); // true
console.log("str".constructor === String); // true
console.log([].constructor === Array); // true
console.log(function () {}.constructor === Function); // true
console.log({}.constructor === Object); // true

// 看个错误的例子
function Fn () {}

Fn.prototype = [];

const f = new Fn();

console.log(f.constructor === Fn); // false
console.log(f.constructor === Array); // true

Object.prototype.toString.call()

前几种方式或多或少都存在一些缺陷,Object.prototype.toString.call() 综合来看是最佳选择,能判断的类型最完整也最准确。

1
2
3
4
5
6
7
8
9
10
11
const oproto = Object.prototype;
const serialize = Object.prototype.toString;

console.log(serialize.call(1)); // [object Number]
console.log(serialize.call(true)); // [object Boolean]
console.log(serialize.call("hello")); // [object String]
console.log(serialize.call([])); // [object Array]
console.log(serialize.call({})); // [object Object]
console.log(serialize.call(() => {})); // [object Function]
console.log(serialize.call(null)); // [object Null]
console.log(serialize.call(undefined)); // [object Undefined]

例如判断一个对象是否为函数只需要使用if (serialize.call(fn) === '[object Function])'即可

其他

其余的还有es6引入的新的Array方法Array.isArray可以直接判断数组类型

综上

  • typeof可以判断基础数据类型,但是遇到null会误判为object,不能用于判断引用数据类型
  • instanceof可以用于检测引用数据类型,原理是根据原型链查找是否为构造函数的实例,不能用于判断基础数据类型
  • constructor直接根据构造函数进行判断类型,但是如果手动更改过对象的prototype,则不准确
  • Object.prototype.toString.call() 方法最好最通用