typeof/instanceof/.toString 检查类型
typeof
相关知识点
typeof 用来判断基本类型(Undefined, null, Boolean, Number, String, bigint, symbol(es6)),最适合用来判断一个值是否为 string/number/boolean/undefined
如果值是对象或 null, 将返回 object
typeof undeclared; // "undefined"
typeof undefined; // "undefined"
typeof 123.1; // "number"
typeof "hello world"; // "string"
typeof false; // "boolean"
typeof null; // "object"
typeof function () {}; // "function"
typeof [1, 2]; // "object"
typeof NaN; // "number"
typeof /^3$/; // "object"
typeof document.all; // "undefined" 这神不神奇
typeof 对 undefined 和 undeclared 变量都返回 undefined
在 V8 中,每一个 Javascript 对象都有一个与之关联的 Map 对象,Map 对象描述 Javascript 对象类型相关的信息,类似元数据 Map 对象主要使用 16 bit 的 instance_type 字段来描述对应 Javascript 对象的类型
ECMA-262 规定,任何实现内部[[Call]]方法的对象都应该在 typeof 检测时返回 function
function 也是 Javascript 的一个内置类型,然而查阅规范就会知道它实际上时 object 的一个“子类型”。具体来说,函数时“可调用对象”。它有一个内部属性[[Call]], 该属性使其可以被调用。 --你不知道的 JavaScript
function 额外知识点
函数也是有属性的,函数对象的 length 属性是其声明的参数的个数
function fn(a, b, c, d) {
return "猜猜我的length";
}
console.log(fn.length); // 4
数组也是 object 的一个“子类型”,数组的元素按数字顺序来进行索引(而非像普通对象那样通过字符串键值),其 length 属性是元素的个数
手写 伪 typeof
undeclared 无法实现
function falseTypeof(val) {
const defaultType = Object.prototype.toString
.call(val)
.slice(8, -1)
.toLowerCase();
const map = {
string: true,
boolean: true,
number: true,
undefined: true,
bigint: true,
symbol: true,
function: true,
};
const isDocumentAll =
String(val) === "document.all" && defaultType === "htmlallcollection";
if (isDocumentAll) {
return "undefined";
}
if (map[defaultType]) {
return defaultType;
}
return "object";
}
v8 实现
很好奇 v8 是怎么实现的, 搜了下大佬们的文章, 上连接
instanceof
刚才看到了 typeof 对一些基本类型值比较有用, 那么引用类型怎么办呢, 别慌, 有 instanceof 在呢
如果是给定引用类型的实例, instanceof 返回 true. 检测基本类型值, 返回 false
举个栗子:
// 结合class
class Person {}
let Tom = new Person();
Tom instanceof Person; // true
// 结合构造函数
function Person() {}
let Tom = new Person();
Tom instanceof Person; // true
// 结合内建class
let arr = [1, 2];
arr instanceof Array; // true
arr instanceof Object; // true
因为 arr 也属于 Object 类, 在原型上 Array 是继承 Object 的
instanceof 在检查的时候会考虑原型链, 还可以通过静态方法Symbol.hasInstance
设置自定义逻辑
// 设置 instanceof 检查
// 假设 有 canDrinkWater 属性的都是 Person
class Person {
static [Symbol.hasInstance](obj) {
if (obj.canDrinkWater) {
return true;
}
}
}
let obj = {
canDrinkWater: true,
};
obj instanceof Person; // true
class 没有 Symbol.hasInstance 的话,会检查 Class.prototype 是否等于对象的原型链中的原型之一
也就是顺着原型链一个一个往上比较往上找
obj.__proto__ === Class.prototype ?
obj.__proto__.__proto__ === Class.prototype ?
obj.__proto__.__proto__.__proto__ === Class.prototype
...
// 找到符合的就返回 true
// 到了原型链尾端还没有找到的话,返回 false
class Person {}
class Tom extends Person {}
let tom = new Tom();
tom instanceof Person; // true
tom.__proto__; // Tom {}
Person.prototype; // Person {}
tom.__proto__.__proto__; // Person {}
Object.prototype.isPrototypeOf()
- isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。
- prototypeObj.isPrototypeOf(object)
isPrototypeOf() 与 instanceof 运算符不同。在表达式 "object instanceof AFunction"中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。 -- MDN
function Person() {}
let tom = new Person();
tom instanceof Person; // true
Person.prototype.isPrototypeOf(tom); // true
// 修改 prototype
Person.prototype = {};
tom instanceof Person; // false
Person.prototype.isPrototypeOf(tom); // false
function A() {}
function B() {}
A.prototype = B.prototype = {};
let a = new A();
a instanceof B; // true
对于 instanceof , 真正决定类型的是 prototype, 而不是构造函数
手写实现 instanceof
方法一:递归
const testInstanceof = (obj, target) => {
if (typeof target !== "function") {
return false;
}
if (obj.__proto__ === null) {
return false;
}
if (obj.__proto__ !== target.prototype) {
return testInstanceof(obj.__proto__, target);
} else {
return true;
}
};
方法二:链表
const testInstanceof = (obj, target) => {
if (typeof target !== "function") {
return false;
}
let p = obj;
while (p) {
if (p.__proto__ === target.prototype) {
return true;
}
p = p.__proto__;
}
return false;
};
Object.prototype.toString
- 将一个普通对象转化为字符串
- 用于检查基本数据类型、内建对象和包含 Symbol.toStringTag 属性的对象
Symbol.toStringTag
- 可以使用特殊的对象属性 Symbol.toStringTag 自定义对象的 toString 方法的行为
let olu = {
[Symbol.toStringTag]: "Olu",
};
Object.prototype.toString.call(olu); // "[object Olu]"
哈哈,感觉这个比 typeof 厉害