JavaScript 有八种数据类型:
基本数据类型:Boolean、Number、String、null、undefined、Symbol(ES6新增)、BigInt(ES2020引入)
引用数据类型:Object
Symbol:表示独一无二的值
BigInt:用来解决 JavaScript 中数字只能到 53 个二进制位,大于这个范围的整数,无法精确表示
底层原理:typeof 是按照"值"在计算机存储的"二进制"值来检测的,凡是以000开始的都认为是对象
返回结果是一个字符串(全小写字母),可返回的类型有:
注意:typeof null
返回的是 “object”,typeof 变量(不存在)
返回的是 “undefined”
typeof null; // "object"
typeof say; // "undefined"(上下文没有定义)
ECMAScript 提供的内置类型在计算机底层是按照二进制存储的
JS 最初为了性能考虑使用低位存储变量信息,000 开头代表对象,然而 null 表示全 0,所以将它错误的判断为 object
底层原理:首先查找
Symbol.hasInstance
,如果存在,基于这个检测。如果没有,则基于原型链__proto__
查找,只要出现这个类的原型,结果就是true
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZefBjyE-1609392039583)(https://gitee.com/lilyn/pic/raw/master/js-img/dir(Function.prototype)].jpg)
class Fn {
// 基于ES6 class方式构建Symbol.hasInstance才会生效
static[Symbol.hasInstance]() {
console.log("OK");
return false;
}
}
let f = new Fn;
console.log(f instanceof Fn); // OK false
注意:
var arr = [1, 2, 3];
arr instanceof Array; // true
arr instanceof Object; // true
null instanceof Object; // false
123 instanceof Number; // false
"" instanceof String; // false
true instanceof Boolean; // false
function instance_of(target, ctor) {
let tType = typeof target,
cType = typeof ctor;
// 保证ctor是一个构造函数
if (cType !== "function" || !ctor.prototype) throw new TypeError("ctor is not a constructor!");
// 不处理原始值,排除null undefined 字面量
if (target == null) return false;
if (!/^(object|function)$/i.test(tType)) return false;
// 优先检测 Symbol.hasInstance
if (typeof ctor[Symbol.hasInstance] === "function") {
return ctor[Symbol.hasInstance](target);
}
// 没有这个属性,再按照 ctor.prototype 是否出现在 example 的原型链上检测
let prototype = Object.getPrototypeOf(target);
while (prototype) {
if (prototype == ctor.prototype) return true;
prototype = Object.getPrototypeOf(prototype);
}
return false;
}
console.log(instance_of([], Array)); // true
console.log(instance_of([], RegExp)); // false
console.log(instance_of([], Object)); // true
constructor 可以得知某个实例对象,到底是哪一个构造函数产生的
**注意:**constructor 可以手动更改(如果手动更改原型指向,检测就不准确了);如果修改了原型对象,一般也会同时修改 constructor。null 和 undefined 是无效的对象,所以不存在 constructor
true.constructor === Boolean; // true
var a = {
};
a.constructor; // Object()
a.constructor = 3;
a.constructor; // 3
底层原理:除了null/undefined,大部分数据类型所属类的原型上,都有toString方法;但是除了 Object.prototype.toString 用来检测数据类型,其余的都是转换为字符串的
- 返回值:"[object ?]"
- 先查找 [val] 的
Symbol.toStringTag
(先找私有的,私有没有则向所属类原型上找),属性值就是"?"的值- 没有,则内部是返回当前实例所属构造函数的名字
“[object Number/String/Null/Undefined/Object/Array/Function/GeneratorFunction...]”
class Fn {
}
Fn.prototype[Symbol.toStringTag] = 'Fn';
let f = new Fn;
console.log(Object.prototype.toString.call(f)); // [object Fn]
对于 Object,直接调用 toString 就可以返回 "[object Object]"
。而对于其他对象,则需要通过 call/apply 来调用才能正确返回类型信息
Object.prototype.toString.call(null); // "[object Null]"
我们可以封装一个 isType 方法对类型进行判断
let isType = (type, obj) =>
Object.prototype.toString.call(obj) === `[object ${
type}]`;
console.log(isType("Number", 12)); // true
思考:每次都写 Object.prototype.toString
是否可以简写?
var obj = {
};
obj.toString.call(null); // "[object Null]"
不过上面还是不够简洁,可否把 obj 直接省去?
obj 省去的话,则默认是 window 调用 toString,我们来看一下 window 的原型链,最终指向 Object
toString.call(null); // 浏览器:"[object Null]"
toString.call(null); // node:[object Null]
进一步验证一下,window 原型链上的 toString 是否和 Object.prototype 上的 toString 一致
window.toString === Object.prototype.toString; // 浏览器:true
global.toString === Object.prototype.toString; // node:true
不过不推荐这么写,毕竟查找原型链还得花上一阵子时间