前言
js有哪些数据类型?
原始类型和引用类型
原始类型:String、Number、Boolean、Undefined、Null、BigInt、Symbol(BigInt、Symbol在上篇文章有介绍)
引用类型:对象、函数、数组、Date(日期对象,返回当前时间)
判断类型有哪几种方法呢?
有四种
- typeof
- instanceof
- object.prototype.toString.call()
- Array.isArray()
typeof
- 可以判断除了null之外的所有原始类型
- 除了function其他所有引用类型都会被判断成object
- typeof通过将值转换为二进制后判断其二进制前三位是否为0,是则为object(对象转换为二进制后的前三位为0,null特殊,转换为二进制全是0,所以会被判成object)
let s = '123'
let n = 123
let b = true
let u = undefined
let nul = null
let sy = Symbol(123)
let big = 999999999999999999999999999999999999999n
let obj = {}
let arr = []
let fn = function () {}
let date = new Date() // 日期对象
console.log(typeof s);
console.log(typeof(n));
console.log(typeof nul, typeof b, typeof u, typeof nul, typeof sy, typeof big);
console.log(typeof obj, typeof arr, typeof fn, typeof date);
打印结果
typeof有两种写法,带()和不带()
console.log(typeof s);
console.log(typeof(n));
instanceof
- 只能判断引用类型
- 通过原型链查找来判断类型 (不了解可以去看看我写的原型和原型链那篇文章)
let s = '123'
let n = 123
let b = true
let u = undefined
let nul = null
let sy = Symbol(123)
let big = 999999999999999999999999999999999999999n
let obj = {}
let arr = []
let fn = function () {}
let date = new Date() // 日期对象
console.log(obj instanceof Object);
console.log(arr instanceof Array);
console.log(fn instanceof Function);
console.log(date instanceof Date);
console.log(s instanceof String);
console.log(arr instanceof Object);
打印结果
分析
instanceof判断类型返回的是布尔值,左边的类型是否是右边,是则返回true,否则返回false。
教你手写一个与instanceof功能相同的方法myinstanceof
function myinstanceof(left, right) {
while(left !== null) {
if(left.__proto__ == right.prototype) {
return true
}
left = left.__proto__
}
return false
}
console.log(myinstanceof([], Array)); // true
console.log(myinstanceof([], Object)); // true
console.log(myinstanceof({}, Array)); // false
打印结果
分析
首先你要了解arr.__proto__ = Array.prototype;Array.prototype.proto = Object.prototype;一直往上找原型找不到了就是null返回false,找得到就把left.__proto__赋给left。
object.prototype.toString.call
object.prototype.toString(this)只能判断对象的类型,且判断有五个步骤
- 如果toString()接收到的值是undefined,抛出错误
- 如果toString()接收到的值是null,抛出错误
- 将 O 作为 ToObject(this) 的执行结果
- 定义一个class作为内部属性 [[class]] 的值
- 返回由 "[object " 和 class 和 "]" 组成的字符串
var a = {}
var b = new Date();
var c = function(){};
var d = [];
var e = 123;
console.log(Object.prototype.toString(a));
console.log(Object.prototype.toString(b));
console.log(Object.prototype.toString(c));
console.log(Object.prototype.toString(d));
console.log(Object.prototype.toString(e));
打印结果
分析
console.log(Object.prototype.toString(e));打印结果为什么不是[object Number]而是[object object],因为toString在接收到一个number类型时会执行ToObject。ToObject会new Number创建一个实例对象,返回的是一个对象。所以会打印出[object object]。所以这个方法不能准确地判断类型。
ToObject的执行规则:
object.prototype.toString.call能判断所有类型
利用了Object.prototype.toString方法,并通过.call()方法改变其内部的this绑定,使其能够应用于任何对象,从而判断该对象的具体类型
var a = {}
var b = new Date();
var c = function(){};
var d = [];
var e = 123;
console.log(Object.prototype.toString.call(a));
console.log(Object.prototype.toString.call(b));
console.log(Object.prototype.toString.call(c));
console.log(Object.prototype.toString.call(d));
console.log(Object.prototype.toString.call(e));
执行结果
教你手写一个mycall方法实现和call一样的效果(深入了解call的原理)
Function.prototype.mycall = function(context) {
// 调用的哥们是不是函数体
if (typeof this !== 'function' ) {
return new TypeError(this + 'is not a function');
}
// this里面的this => context
const fn = Symbol('key');
context[fn] = this;// 让对象拥有该函数 {Symbol('key'): foo}
context[fn]();// 触发了隐式绑定
delete context[fn];
}
var obj = {
a:1
}
function foo() {
console.log(this.a);
}
foo.mycall(obj);
打印结果
分析
因为mycall方法是实现类似于call方法,即调用该方法的函数foo的this指到mycall接收到的对象obj上面去。其实mycall是通过让obj增加一个属性,其属性的值为this属性名为Symbol创建的一个独一无二的键名,这个this就是调用mycall方法的函数,为了让obj对象拥有该函数foo,然后再让对象调用该函数触发隐式绑定规则,这样调用mycall方法的函数foo的this就指向了obj,最后再清除obj内的foo函数。
Array.isArray
只用于判断某个变量是否为数组类型的一个内置函数
let a = [];
let b = {};
let c = 123;
console.log(Array.isArray(a));
console.log(Array.isArray(b));
console.log(Array.isArray(c));
执行结果
结语
类型判断是JavaScript编程中的基础而又重要的环节,灵活运用各种方法,并根据具体需求和环境选择最合适的做法,将有助于编写更加健壮和高效的代码。随着语言的演进,持续学习新的特性和最佳实践也是每个开发者不可或缺的部分。