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 厉害