引言文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
在JavaScript中,准确地判断变量的类型是编程中不可或缺的一部分。无论是进行数据验证、类型转换还是实现复杂的逻辑,掌握类型判断的方法都是非常重要的。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
本文将带你深入了解四种常用的类型判断方法:typeof
、instanceof
、Object.prototype.toString.call
以及 Array.isArray
。通过这些方法,你可以更精确地识别变量的类型,为你的JavaScript开发之路添砖加瓦。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
一、typeof
typeof
操作符用于判断变量的类型,尤其适用于原始类型。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
判断原始类型(除 null
之外)
首先,我们要知道原始类型目前一共有7种:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
js
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 大整型 超过2的53次方
console.log(typeof (s));//string
console.log(typeof (n));//number
console.log(typeof (f));//boolean
console.log(typeof (u));//undefined
console.log(typeof (sy));//symbol
console.log(typeof (big));//bigint
//另类
console.log(typeof (nu));//object
文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
判断引用类型
我们再来看下面的对象判断出的类型是什么:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
js
let fn = function () { }
let obj = {}
let arr = []
let date = new Date()
console.log(typeof (fn));//function
console.log(typeof (obj));//object
console.log(typeof (arr));//object
console.log(typeof (date));//object
文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
可以看到对function
和对象obj
可以准确判断出,数组arr
判断出的类型为object
(但是数组也确实属于对象,说得过去),日期类型date
也为object
文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html
从上面我们可以总结出:
typeof
可以准确判断出的基本数据类型有:number
、string
、boolean
、undefined
、symbol
、bigint
和function
。 对于null
和其他的引用类型,typeof null
返回"object"
,这到底是怎么回事呢??
判断null
返回 "object"
???
这,其实是一个 JavaScript 的历史遗留下来的bug。
在 JavaScript 的早期版本中,值在底层是以二进制形式存储的,并且变量的类型是通过检查其二进制表示的前几位来确定的。如果前几位是 000
,那么就判定为是对象类型。而null
的二进制值又全是0,那必然它的前几位也是 000
,所以才被错误地判定为 object
。
虽然这个bug早已被认识到,但由于历史原因和兼容性考虑,这一行为在 JavaScript 中一直没有被修改。所以到现在typeof null
返回 "object"
被视为 JavaScript 语言的一个历史遗留问题。
二、instanceof
原始类型我们实现了精确判断(当然,除了null),那我们想要精确判断引用类型用什么呢?
没关系,它来了,它就是instanceof
!这位选手可以准确检测出对象是否是某个构造函数的实例。
标准用法
js
let obj = {};
let arr = [];
let date = new Date();
let fn = function() {};
console.log(obj instanceof Object); // true
console.log(arr instanceof Array); // true
console.log(date instanceof Date); // true
console.log(fn instanceof Function); // true
“非标准用法”
如果用instanceof
判断原始类型呢?试试!
试试就逝世
js
let s = '123'
let n = 123
console.log(s instanceof String);
console.log(n instanceof Number);
诶,都说了只能判断引用类型啦
实现原理
在细说instanceof
的实现原理之前,我们先来看看这段代码输出什么:
js
let arr = []
console.log(arr instanceof Object);
是的没错,数组属于对象,输出结果为true,正确的,客观的,中肯的。
这样我们大概就能推测出instanceof
是通过原型链进行判断的
啥?不记得圆形脸啦?复习一下!
原型链的工作原理
在 JavaScript 中,每个对象都有一个内部属性 [[Prototype]]
(可以通过 __proto__
访问)指向其构造函数的 prototype
对象。这个 prototype
对象本身也有一个 [[Prototype]]
,最终连接到 Object.prototype
。这种链式结构形成了原型链。
嗦哒嘶内~
复习完了我们来看看这段:
- p 属于 Person
检查对象 p
是否在 Person
的原型链上。由于 p
是通过 new Person()
创建的,p
是 Person
构造函数的实例,因此 p instanceof Person
返回 true
。
- p 属于 Object
在 JavaScript 中,所有对象(包括通过构造函数创建的对象)都继承自 Object.prototype
。由于 Person
构造函数本身也是一个对象,其原型链最终会连接到 Object
的原型链上。所以,p
也会在 Object
的原型链上。因此,p instanceof Object
返回 true
。
底层逻辑
知道了原理后,深入了解instanceof
底层逻辑就不难了
js
function myInstanceof(L, R) {
// 获取L的原型
let proto = L.__proto__;
// 获取R的prototype对象
let prototype = R.prototype;
// 判断对象是否是某个构造函数的实例
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = proto.__proto__;
}
return false;
}
// 测试代码
console.log(myInstanceof([1], Array));//ture
console.log(myInstanceof({}, Array));//false 对象不是数组,就像四边形不是正方形
console.log(myInstanceof([], Object));//ture
小结
给instanceof
总结一下:
- 只能判断引用类型
instanceof
用于判断引用类型,不能用于判断原始类型。
- 通过原型链判断
instanceof
检查构造函数的prototype
属性是否出现在对象的原型链上。所以instanceof
才不能判断原始类型,原始类型人家压根没原型啊。
ps.
对象的原型叫对象原型,也叫隐式原型;
函数的原型叫原型,也叫显示原型.
三、Object.prototype.toString.call()
Object.prototype.toString.call()
是一种更加精确的类型判断方法。
使用方法
js
Object.prototype.toString.call(123); // "[object Number]"
Object.prototype.toString.call("hello"); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call(new Date()); // "[object Date]"
判断得非常精准
方法解释
这个方法在官方文档的位置为es5.github.io/#x15.2.4.2
好,全是英文,让我们打开翻译
还有点懵?没关系,我们看下面的步骤讲解
1.判断是否为 null
或 undefined
- 对于
null
和undefined
,Object.prototype.toString
分别返回"[object Null]"
和"[object Undefined]"
。
例子:
js
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
2. 将其他值转换为对象并判断类型
- 对于其他类型,
Object.prototype.toString
通过内部属性[[Class]]
返回一个格式为"[object Type]"
的字符串,其中Type
是对象的类型。
我们点击第3点里的ToObject跳转到页面:
详细解释
1) 未定义 (Undefined) 如果传递的参数是 undefined
,则引发 TypeError 异常。
2) 无效的 (Null)
如果传递的参数是 null
,则引发 TypeError 异常。
3) 布尔值 (Boolean)
如果传递的参数是布尔值(如 true
或 false
),则创建一个新的布尔对象(Boolean Object)。这个对象的 [[PrimitiveValue]]
内部属性被设置为布尔值本身。例如,ToObject(true)
会返回一个 Boolean 对象,其内部属性 [[PrimitiveValue]]
为 true
。
4) 数字 (Number)
如果传递的参数是数字(如 42
),则创建一个新的 Number 对象。这个对象的 [[PrimitiveValue]]
内部属性被设置为数字值本身。例如,ToObject(42)
会返回一个 Number 对象,其内部属性 [[PrimitiveValue]]
为 42
。
5) 字符串 (String)
如果传递的参数是字符串(如 "hello"
),则创建一个新的 String 对象。这个对象的 [[PrimitiveValue]]
内部属性被设置为字符串值本身。例如,ToObject("hello")
会返回一个 String 对象,其内部属性 [[PrimitiveValue]]
为 "hello"
。
6)对象 (Object)
如果传递的参数本身就是一个对象,则直接返回这个对象(无转换)。例如,ToObject({a: 1})
会直接返回 {a: 1}
。
3. 显示绑定
- 通常,
Object.prototype.toString
直接调用时,其上下文对象 (this
) 是Object.prototype
,返回的结果是[object Object]
。通过使用call
方法,可以显式地指定this
的值,从而准确判断传入变量的类型。
模拟(简单版)
js
Function.prototype.mycall = function(context, ...args) {
// 确认调用的对象是否是函数
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function');
}
// 确保 context 不为 null 或 undefined,如果是,则设置为全局对象(在浏览器中为 window)
context = context || globalThis;
// 创建一个唯一的符号属性,以防止覆盖已有属性
const fn = Symbol('key');
// 将函数(this)作为属性添加到传入的对象(context)
context[fn] = this;
// 调用该函数,并传递额外的参数
const result = context[fn](...args);
// 删除刚才添加的属性
delete context[fn];
// 返回函数的返回值
return result;
}
// 测试代码
var obj = {
a: 1
}
function foo(b, c) {
console.log(this.a, b, c);
}
foo.mycall(obj, 2, 3); // 输出:1 2 3
console.log(obj); // 输出:{ a: 1 }
四、Array.isArray
Array.isArray()
是一个非常直接且简单的方法,它用于检查一个给定的值是否是 JavaScript 中的数组。如果该值是一个数组就返回 true
,否则返回 false
。
这种方法特别有用,因为它可以正确地区分数组和其他类型的对象,例如对象字面量或函数。
在早期版本的 JavaScript 中,人们通常使用 typeof
操作符或者 constructor
属性来尝试判断一个值是不是数组,但这两种方法都有其局限性。
局限性
typeof
:返回的是"object"
而不是"array"
;
constructor
:如果某个对象的原型链被修改过,那么constructor
可能会被改变。
例子:
js
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray("hello")); // false
console.log(Array.isArray(new Date())); // false
console.log(Array.isArray(function() {})); // false
console.log(Array.isArray([1, 2, 3])); // true
评论