Javascript数据类型判断“浅谈”

123 阅读4分钟

前置知识

javascript类型

  • 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol。
  • 引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。

类型判断的方法

掌握类型判断:typeof、instanceof、Object.prototype.toString.call、constructor

typeof

typeof 运算符返回一个字符串,表示操作数的类型。

类型结果
String"string"
Number"number"
Boolean"boolean"
Undefined"undefined"
Symbol"symbol"
BigInt"bigint"
Null"null"
Function"function"
Object"object"
Array"object"
Date"object"
RegExp"object"

示例

typeof 37 === 'number';
typeof 3.14 === 'number';
typeof(42) === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值
typeof Number("shoe") === 'number'; // 包括不能将类型强制转换为数字的值

typeof 42n === 'bigint';

// 字符串
typeof '' === 'string';
typeof 'bla' === 'string';
typeof `template literal` === 'string';
typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串
typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串
typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全

// 布尔值
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!(1) === 'boolean'; // 两次调用 !(逻辑非)运算符相当于 Boolean()

// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';

// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined';

// 对象
typeof { a: 1 } === 'object';

// 使用 Array.isArray 或者 Object.prototype.toString.call
// 区分数组和普通对象
typeof [1, 2, 4] === 'object';

typeof new Date() === 'object';
typeof /regex/ === 'object';

// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';

// 函数
typeof function() {} === 'function';
typeof class C {} === 'function'
typeof Math.sin === 'function';

//Null
// JavaScript 诞生以来便如此
typeof null === "object";

总结 注意

  • typeof对基本类型可以做出准确判断,除了Null
  • 所有使用new调用的构造函数都将返回非基本类型("object" 或 "function"
const num = new Number(100);

typeof str; // "object"
typeof num; // "object"

const func = new Function();

typeof func; // "function"

  • 未声明的标识符,typeof 也会返回 "undefined",而不是抛出错误。
typeof undeclaredVariable; // "undefined"

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

示例

console.log({} instanceof Object);//true
console.log(function(){} instanceof Function);//true
// 如果后面是Object的话,那么都返回true,因为Object是它们的祖师爷
console.log([] instanceof Object);//true
console.log({} instanceof Object);//true
console.log(function(){} instanceof Object);//true

var a =1;
console.log(a instanceof Number);//false
console.log(a.constructor === Number);//true
// 这里有个坑!其实 a是Number的实例,Number的原型也在a的原型链上!为什么 
// a instanceof Number结果是false? 其实是 object instranceof constructor ,
// 注意看!是object,如果不是object全部返false,
// 那么问题来了?为什么实例出来的对象变成了基本类型?
// 很懵!!! 其实当我们字面量创建变量时候,后台就会创建一个对应的基本包装类型的对象,
// 下面是它的创建过程!
// 1.创建String的实例
// 2.在实例上调用指定的方法
// 3.销毁这个实例
var a = "1234" 
//等价于
var a = new Number("1234");
var copyA = a.substr(0);
a = null;
// 这个过程是非常短暂的,这个也是引用类型和包装类型的最大区别,也就是对象的生命周期,
// 在运行的一瞬间就被销毁了,这个也是为什么在运行的时候在包装对象上添加不了属性和方法;
var a = "1234"
a.add = "add";
console.log(a.add);//underfined

总结

  • instanceof 可以判断引用类型,但是判断不了基本类型
  • instanceof a instranceof b,意思判断 b的原型对象是否在a的原型链上"object instranceof constructor"

Object.prototype.toString

toString()  方法返回一个表示该对象的字符串。该方法旨在重写(自定义)派生类对象的[类型转换]的逻辑。

// 能够比较精准的判断各种类型,原理是借用Object原型上的toString()方法!
var test = Object.prototype.toString;
console.log(test.call(1));//[object Number]
console.log(test.call("1"));//[object String]
console.log(test.call(true));//[object Boolean]
console.log(test.call(function(){}));//[object Function]
console.log(test.call(undefined));//[object Undefined]
console.log(test.call(null));//[object Null]
console.log(test.call([]));//[object Array]

constructor

构造函数属于被实例化的特定类对象 。构造函数初始化这个对象,并提供可以访问其私有信息的方法。构造函数的概念可以应用于大多数面向对象的编程语言。本质上,JavaScript 中的构造函数通常在[类]的实例中声明。

示例

// 每个实例对象都从原型中继承了一个 constructor 属性,该属性指向了用于构造此实例对象的构造函数。
console.log((1).constructor ===Number);//true
console.log(true.constructor === Boolean);//true
console.log("1".constructor === String);//true
console.log([].constructor === Array);//true
console.log(function(){}.constructor === Function);//true
console.log({}.constructor === Object);//true

// 有一点需要注意,当直接给原型赋值后,那么constructor指向就会改变!
function A(){};
var p =new A();
console.log(p.constructor===A);//true
A.prototype = [];
var p1 =new A();
console.log(p1.constructor===A);//false
console.log(p1.constructor===Array);//true

总结

  • 每个实例对象都从原型中继承了一个 constructor 属性,该属性指向了用于构造此实例对象的构造函数。
  • 有一点需要注意,当直接给原型赋值后,那么constructor指向就会改变!

手写自己的方法

由上述可见这些方法大都有自己的弊端,那么我们为了更好的实现类型判断,借助相对完善的toString方式下面我们就来实现一个自己的方法

function myTypeof(obj){
    if(obj===null) return "null"
    if(typeof obj !== "object" ){
        return typeof obj
    } else{
        return Object.prototype.toString.call(obj).slice(8, -1).toLocaleLowerCase()
    }
}