js中的数据类型检测

940 阅读4分钟

blog.png

前言

以下内容是自我学习和整理,如有错误欢迎指出

- typeof

typeof 操作符返回一个字符串,表示未经计算的操作数的类型. 用来判断基本数据类型

MDN

类型结果
Undefined"undefined"
Null"object"
Boolean"boolean"
Number"number"
BigInt(ECMAScript 2020 新增)"bigint"
String"string"
Symbol (ECMAScript 2015 新增)"symbol"
宿主对象(由 JS 环境提供)取决于具体实现
Function 对象 (按照 ECMA-262 规范实现 [[Call]])"function"
其他任何对象"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 会尝试把参数解析成数值

// Bigint
typeof 42n === 'bigint';

//当前所有的浏览器都暴露了一个类型为 undefined 的非标准宿主对象
typeof document.all === 'undefined';

// 字符串
typeof '' === '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';

// 区分数组和普通对象
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
typeof null === 'object'; // JavaScript 诞生以来便如此,在JavaScript最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 `null` 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,`typeof null` 也因此返回 `"object"`

/**
 * 在ECMAScript2015之前,typeof总能保证对任何所给的操作数返回一个字符串。
 * 即便是没有声明的标识符,typeof 也能返回 'undefined'。使用typeof永远不会抛出错误。
 * 但在加入了块级作用域的[let]之后,在其被声明之前对块中的 let 和 const 变量使用 
 * typeof 会抛出一个[ReferenceError]块作用域变量在块的头部处于“[暂存死区]”直至其被初始化,在这期间,访问变量将会引发错误。
 */
typeof undeclaredVariable === 'undefined';
typeof newLetVariable; // ReferenceError
typeof newConstVariable; // ReferenceError
typeof newClass; // ReferenceError

let newLetVariable;
const newConstVariable = 'hello';
class newClass{};

- instanceof

instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。


/**
 * 需要注意的是,如果表达式 obj instanceof Foo 返回 true,则并不意味着该表达式会永远返回 true,
 * 因为 Foo.prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 obj 的原型链上,
 * 这时原表达式的值就会成为 false。另外一种情况下,原表达式的值也会改变,就是改变对象 obj 的原型链的情况,
 * 虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 __proto__ 伪属性,是可以实现的。
 * 比如执行 obj.__proto__ = {} 之后,obj instanceof Foo 就会返回 false 了。
 */
// 定义构造函数
function C(){}
function D(){}

var o = new C();

o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype

o instanceof D; // false,因为 D.prototype 不在 o 的原型链上

o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object // true,同上

C.prototype = {};
var o2 = new C();

o2 instanceof C; // true

o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.

D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上

- 手写实现instanceof


/**
 * 
 * @param {*} obj 实例对象
 * @param {*} func 构造函数
 * @return true / false
 */
 const myInstanceOf = (obj, func) => {
    if (obj === null || typeof obj !== 'object') {
      return false
    }
    
    let proto = Object.getPrototypeOf(obj)
    
    if (proto === func.prototype) {
      return true
    } 
    if (proto === null) {
      return false
    } 
    return myInstanceOf(proto, func)
  }
  
  // 测试
  let Fn = function () { }
  let instance = new Fn()
  
  console.log(myInstanceOf({}, Object)) // true
  console.log(myInstanceOf(instance, Fn)) // true
  console.log(myInstanceOf({}, Fn)) // false
  console.log(myInstanceOf(null, Fn)) // false
  

- Object.prototype.toString()

toString()  方法返回一个表示该对象的字符串。

/**
 * 可以通过 toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,
 * 需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,
 * 传递要检查的对象作为第一个参数,称为 thisArg。
 */
let toString = Object.prototype.toString;

toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]

//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]

用toString()封装一个判断数组或者对象

const isType = type => target => Object.prototype.toString.call(target) === `[object ${type}]`

const isArray = isType('Array')

const isObject = isType('Object')

//测试
console.log(isArray([]))  // true
console.log(isObject({})) // true
console.log(isObject(null)) // false