引言
在JavaScript编程中,类型判断是我们经常会遇到的问题。对于初学者来说,有时候可能会困惑于如何准确地判断一个变量的类型。今天,我们就来揭开JavaScript中常用的几种类型判断方法及其原理,帮助大家更好地理解和运用。
1. typeof 操作符
首先,让我们来介绍一下最常用的类型判断方法之一:typeof操作符。通过typeof操作符,我们可以得知一个变量是什么基本类型,比如是字符串、数字、布尔值、函数等。它的语法是:typeof 变量名。值得注意的是,typeof操作符返回的结果是一个字符串,表示被检测变量的数据类型。
直接上几个代码示例:
let s = '123' // string
let n = 123 // number
let f = true // boolean
let u = undefined // undefined
let nu = null // null
let sy = Symbol(123) // Symbol
let big = 1234n // BigInt
let obj = {}
let arr = []
let fn = function() {}
let date = new Date()
console.log(typeof(s)); // string
console.log(typeof(n)); // number
console.log(typeof(f)); // boolean
console.log(typeof(u)); // undefined
console.log(typeof(nu)); // object
console.log(typeof(sy)); // symbol
console.log(typeof(big)); // bigint
console.log(typeof(obj)); // object
console.log(typeof(arr)); // object
console.log(typeof(fn)); // function
console.log(typeof(date)); // object
判定规则
- typeof 可以判断
除null以外的 所有基本数据类型除了function,其它所有引用数据类型,都是object
内部原理
相信你一定有两个疑问
- 为什么function返回的不是object?
- 为什么null会返回object?
首先我们来解答第一个问题,因为typeof的内部原理是:
- JS引擎为每种数据类型分配了特定的内部类型标识。这些标识通常与ECMAScript规范中定义的类型相对应,包括
Undefined,Null,Boolean,Number,String,Symbol,BigInt,Object, 以及特殊的Function- typeof在接受参数后,会将其转化为二进制,基本数据类型前3位一定不是0,引用数据类型前3位为0
- 不是0进行进一步的转换,得到具体的类型
- 前3位是0,如果具有[[Call]](调用),则为
Function,否则为Object
所以因为function具有[[Call]](调用),所以返回的不是object。
那么,第二个问题。其实这是一个bug,因为js官方在设计这门语言的时候将null的二进制编码设置成了全零,所以会造成返回Object。
所以,如果要判断一个变量是不是object,需要加上不等于null的特判
function isObject(obj) {
return typeof obj === 'object' && obj !== null;
}
2. instanceof 操作符
除了typeof操作符,我们还可以使用另一种方法进行类型判断,那就是instanceof操作符。instanceof操作符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。如果在原型链上找到了对应的构造函数,则返回true,否则返回false。
let s = '123' // string
let n = 123 // number
let f = true // boolean
let u = undefined // undefined
let nu = null // null
let sy = Symbol(123) // Symbol
let big = 1234n // BigInt
let obj = {}
let arr = []
let fn = function () { }
let date = new Date()
console.log(obj instanceof Object); //true
console.log(arr instanceof Array); //true
console.log(fn instanceof Function);//true
console.log(date instanceof Date);//true
console.log(s instanceof String);//false
console.log(n instanceof Number);//false
console.log(arr instanceof Object);//true
判断规则
- 只能判断引用数据类型
- 只会返回true或false,检测构造函数的
prototype属性是否出现在某个实例对象的原型链上
内部原理
其实就是通过原型链查找来判断类型。
我们可以通过手写一个myinstanceof来更好的理解它的原理。
function myinstanceof(a, b) {
while (a) {
if (a.__proto__ === b.prototype) return true;
a = a.__proto__;
}
return false;
}
console.log(myinstanceof([], Array));//true
console.log(myinstanceof([], Object));//true
其实,就是不断查找a的隐式原型的隐式原型...(不断往下)是否等于的b的显示原型,如果等于则返回true。
如果您对JS原型有困惑,建议您查看我之前写的文章《干货分享(三)——深入理解JS原型和原型链》,这可以帮助您更深入理解和理顺JS中原型和原型链的概念。
3. Object.prototype.toString.call() 方法:
再来介绍一个更加严谨和通用的类型判断方法:Object.prototype.toString.call()。它可以准确地判断 JavaScript 中的数据类型,甚至包括了对于 Object 的具体子类型的判断。例如:
Object.prototype.toString.call(42); // "[object Number]"
Object.prototype.toString.call("hello"); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call({key: 'value'}); // "[object Object]"
Object.prototype.toString.call(function() {}); // "[object Function]"
内部原理
在了解Object.prototype.toString.call()之前我们先要了解Object.prototype.toString() 和 call() 的原理
1 .Object.prototype.toString() 原理
在JSAnnotated ES5官方文档中的解释为:
翻译过来就是:
- 如果this为undifined,则返回“[object Undefined]”。
- 如果this为null,则返回“[object Null ]”。
- 设 O 为 ToObject(this) 的结果,并将此值作为参数传递。
- 定义一个Class,为 O 的[[ Class ]]的值。
- 返回由“[ object” 和 class 和 “]” 组成的字符串
而对于ToObject(this),官方解释为:
简单来说就是:
- 如果是Undifined或者Null,抛出一个TypeError错误
- 如果是Boolean、Number、String 创建对应的对象返回
- 如果是Object不变
2 .call(context) 核心原理
我们通过一个手写的简易版的mycall方法来了解他的原理,其实就是利用了隐式绑定,将this强行指向你指定的context。
Function.prototype.mycall = function (context) {
// 调用我的是不是函数体,只有函数才能使用call方法
if (typeof this !== 'function') {
return new TypeError(this + 'is not a function');
}
// 利用this隐式绑定规则
const fn = Symbol('key') // 防止使用时冲突
context[fn] = this; // 让对象拥有该函数
context[fn](); // 触发 隐式绑定
// 删除刚刚添加的fn属性
delete context[fn];
}
如果您对JS中this指向有困惑,建议您查看我之前写的文章《深入探究:一篇文章搞懂JS this指向》,这将帮助您更好地理解this在JS中的指向规则。
3. Object.prototype.toString.call() 原理
现在你已经知道了Object.prototype.toString() 和 call() 的原理,但其实如果你单独使用Object.prototype.toString(),只要不输入undifined,返回的都是 [object Object] 。
这是因为Object.prototype.toString()单独使用时,会触发隐式绑定规则,使得this指向Object.prototype,也就是一个object。所以按照它自己的规则,规则1,2,3都不会对它产生任何影响,规则4就会取到它自己的[[ Class ]],从而输出[object Object]。
而使用call,会像this强行指向你传入的参数,以Number类型为例:
Object.prototype.toString()的规则3就会返回一个Number对象,从而返回Number对象的[[ Class ]],而不是object的[[ Class ]],所以能返回正确的结果[object Number]
4. Array.isArray(x)
最后一个我们要介绍的是用于判断数组类型的方法:Array.isArray()。它在判断一个变量是否为数组时非常方便,如果是数组则返回 true,否则返回 false。让我们来看一个例子:
Array.isArray([1, 2, 3]); // true
Array.isArray("hello"); // false
只能用于数组检测
内部原理
在JS引擎内部,Array.isArray()函数会检查传入值的[[Class]]内部属性,这是一个内部元数据属性,用于标识对象的类型。对于数组实例,这个属性的值为"Array"。Array.isArray()正是通过比较这个内部属性来判断一个值是否为数组。
结语
以上就是JavaScript中常用的几种类型判断方法及其原理介绍。希望通过本文的分享,大家能够更清晰地了解这些方法的使用及底层原理,从而在实际编程中更加灵活自如地应用。如果你觉得这篇文章有帮助或有所启发,别忘了给我一个鼓励的赞!