JavaScript 有八种数据类型:
基本数据类型:Boolean、Number、String、null、undefined、Symbol(ES6新增)、BigInt(ES2020引入)
引用数据类型:Object
Symbol:表示独一无二的值
BigInt:用来解决 JavaScript 中数字只能到 53 个二进制位,大于这个范围的整数,无法精确表示
typeof
底层原理:typeof 是按照”值”在计算机存储的”二进制”值来检测的,凡是以 000 开始的都认为是对象
返回结果是一个字符串(全小写字母),可返回的类型有:
- “number”
- “string”
- “boolean”
- “undefined”
- “object”
- “function”
- “symbol”
- “bigint”
注意: typeof null
返回的是 “object”,typeof 变量(不存在)
返回的是 “undefined”
1 | typeof null; // "object" |
ECMAScript 提供的内置类型在计算机底层是按照二进制存储的
- 1:数字 010:浮点数
- 100:字符串
- -2^30:undefined
- 000000:null
- 000:对象
JS 最初为了性能考虑使用低位存储变量信息,000 开头代表对象,然而 null 表示全 0,所以将它错误的判断为 object
instanceof
底层原理:首先查找
Symbol.hasInstance
,如果存在,基于这个检测。如果没有,则基于原型链__proto__
查找,只要出现这个类的原型,结果就是 true
基于ES6 class 方式设置静态私有属性构建 Symbol.hasInstance
才会生效
1 | class Fn { |
注意:
- instanceof 不能正确判断基本数据类型
- 原型链可以重构,导致结果可能不准确
1 | [] instanceof Array // true |
封装 instanceOf
1 | function instance_of(obj, Ctor) { |
constructor
constructor 可以得知某个实例对象,到底是哪一个构造函数产生的
注意:constructor 可以手动更改(如果手动更改原型指向,检测就不准确了);如果修改了原型对象,一般也会同时修改 constructor。null 和 undefined 是无效的对象,所以不存在 constructor
1 | true.constructor === Boolean; // true |
Object.prototype.toString.call()
底层原理:除了 null/undefined,大部分数据类型所属类的原型上,都有toString方法;但是除了
Object.prototype.toString
用来检测数据类型,其余的都是转换为字符串的
- 返回值:”[object ?]”
- 先查找 [val] 的
Symbol.toStringTag
(先找私有的,私有没有则向所属类原型上找),属性值就是”?”的值- 没有,则内部是返回当前实例所属构造函数的名字
“[object Number/String/Null/Undefined/Object/Array/Function/GeneratorFunction...]”
1 | let class2type = {}, |
我们可以封装一个 isType 方法对类型进行判断
1 | let isType = (type, obj) => |
思考:每次都写 Object.prototype.toString
是否可以简写?
- 如果调用 toString 方法,obj 首先会在自己私有方法里找,如果没有则顺着原型链往上找,所以 prototype 可以省略
1 | var obj = {}; |
不过上面还是不够简洁,可否把 obj 直接省去?
- 浏览器全局环境:window(是一个对象)
- node全局环境:global(是一个对象)
obj 省去的话,则默认是 window 调用 toString,我们来看一下 window 的原型链,最终指向 Object
1 | toString.call(null); // 浏览器:"[object Null]" |
进一步验证一下,window 原型链上的 toString 是否和 Object.prototype 上的 toString 一致
1 | window.toString === Object.prototype.toString; // 浏览器:true |