本文深入解析JavaScript中判断数据类型的三种核心机制:typeof、instanceof以及Object.prototype.toString。通过剖析原型链查找逻辑与[[Class]]属性获取方式,阐明各方法的适用场景与局限性,并结合valueOf隐式转换规则,提供类型检测的最佳实践方案。
typeof
typeof 操作符的核心功能是识别数据的基本类型。
| 类型 | typeof 结果 | |
|---|---|---|
| 基本类型 | undefined | undefined |
| Boolean | boolean | |
| Number | number | |
| String | string | |
| Symbol | symbol | |
| BigInt | bigint | |
| null | object | |
| 引用类型 | Object(Object, Array, Map, Set, Regexp, Date 等) | object |
| Function | function |
当使用 typeof 检测引用类型时,结果通常统一返回 'object'。针对这一局限性,引入了 instanceof 运算符进行补充检测。
instanceof 运算符用于验证构造函数的 prototype 属性是否存在于实例对象的原型链中。
var arr = [];
arr instanceof Array; // true
typeof arr; // "object"
function A() {}
function B() {}
// Javascript 继承
B.prototype = new A();
var b = new B();
b instanceof A; // true
b instanceof B; // trueinstanceof 的内部实现原理
- 核心思路:
基于原型与原型链机制:每个函数拥有显式的 prototype 属性,每个对象拥有隐式原型 __proto__。若对象的原型链中能找到构造函数的显式 prototype,即可判定两者存在关联;
function myInstanceOf(constructor, instance) {
let prototype = constructor.prototype;
let proto = instance.__proto__;
while (true) {
// 说明道原型链订单, 还未找到, 返回 false
if (proto === null) {
return false;
}
if (proto === prototype) {
return true;
}
// 继续向 proto 的原型链上遍历
proto = Object.getPrototypeOf(proto);
}
}Object.prototype.toString()
- toString() 方法返回表示该对象的字符串。该方法旨在重写(自定义)派生类对象的类型转换逻辑。
- valueOf() 方法返回对象的原始值表示。
该方法由字符串转换优先调用,但数字的强制转换和原始值的强制转换会优先调用 valueOf,因为基本的 valueOf() 方法返回一个对象,toString() 方法通常在结束时调用。
默认情况下(不重写 toString 方法),任何对象调用 Object 原生的 toString 方法,都会返回一个 [object type],其中 type 是对象的类型。
let a = {};
a; // {}
a.toString(); // "[object Object]"[[class]]
每个实例都有一个 [[Class]] 属性,该属性指定了上述字符串中的 type。[[Class]] 不能直接访问,但通常可以通过 Object.prototype.toString.call(..) 方法调用来展示。
// Boolean 类型,tag 为 "Boolean"
Object.prototype.toString.call(true); // => "[object Boolean]"
// Number 类型,tag 为 "Number"
Object.prototype.toString.call(10); // => "[object Boolean]"
// String 类型,tag 为 "String"
Object.prototype.toString.call("1312312"); // => "[object String]"
// Array 类型,tag 为 "String"
Object.prototype.toString.call([]); // => "[object Array]"
// Function 类型, tag 为 "Function"
Object.prototype.toString.call(function () {}); // => "[object Function]"
// Error 类型(包含子类型),tag 为 "Error"
Object.prototype.toString.call(new Error()); // => "[object Error]"
// RegExp 类型,tag 为 "RegExp"
Object.prototype.toString.call(/\d+/); // => "[object RegExp]"
// Date 类型,tag 为 "Date"
Object.prototype.toString.call(new Date()); // => "[object Date]"
// 其他类型,tag 为 "Object"
Object.prototype.toString.call(new (class {})()); // => "[object Object]"因此,可以利用此方法来判断每个对象的类型。
function generator(type){
return function(value){
return Object.prototype.toString.call(value) === "[object "+ type +"]"
}
}
let isFunction = generator('Function')
let isArray = generator('Array');
let isDate = generator('Date');
let isRegExp = generator('RegExp');
isArray([])); // true
isDate(new Date()); // true
isRegExp(/\w/); // true
isFunction(function(){}); //true以下是一道简单的面试题
+[1 + [2] + 3] + [1 + 2 + true - false] + [[3 - false + "1"]];
// 拆分一下
let a = +[1 + [2] + 3];
// [2]会首先进行转换 [2].valueOf, 结果不是基本类型, [2]在调用toString(), 返回'2', 最后 1 + '2' + 3, 1,3进行隐式转换, +'123' ==> 123
let b = [1 + 2 + true - false];
// [3+true-false], true, false会进行转换, true=>1, false=>0, 最后1-0==>[1+3]==>[4]==>[4].valueOf().toString()==>'4'
let c = [[3 - false + "1"]];
// 先转换数组里面的一层, [3-0+'1']==>['31'], 结果: [['31']]==>[['31']].valueOf().toString()==>'31'
// 最后 a+b+c
// 123+'4'+'31'==>123431面试题目:如何同时让等式成立 a===1&&a===2&&a===3
思路:
重写 a 的 valueOf 方法
let a = {
value: [3, 2, 1],
valueOf: function () {
return this.value.pop();
},
};总结
- 当一侧为 String 类型,被识别为字符串拼接,并会优先将另一侧转换为字符串类型。
- 当一侧为 Number 类型,另一侧为原始类型,则将原始类型转换为 Number 类型。
- 当一侧为 Number 类型,另一侧为引用类型,将引用类型和 Number 类型转换成字符串后拼接。
- 只有 null undefined '' NaN 0 false 这几个是 false,其他的情况都是 true,比如 {} , []。
以上就是js类型判断内部实现原理示例详解的详细内容,更多关于js类型判断内部原理的资料请关注本站其它相关文章!