javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JavaScript数据检测方法

JavaScript数据检测方法的全面指南

作者:绅士玖

在 JavaScript 开发中,准确检测数据类型是保证代码健壮性的基础,JavaScript 提供了多种数据类型检测方法,每种方法都有其适用场景和局限性,本文将全面介绍 JavaScript 中各种数据检测方法,需要的朋友可以参考下

前言

在 JavaScript 开发中,准确检测数据类型是保证代码健壮性的基础。JavaScript 提供了多种数据类型检测方法,每种方法都有其适用场景和局限性。本文将全面介绍 JavaScript 中各种数据检测方法,包括它们的实现原理、使用场景、优缺点比较,以及如何手动实现一些核心检测方法如 instanceof

一、typeof 操作符

1. 基本用法

typeof 是最常用的类型检测操作符,返回一个表示数据类型的字符串。

console.log(typeof 42);          // "number"
console.log(typeof 'str');      // "string"
console.log(typeof true);       // "boolean"
console.log(typeof undefined);  // "undefined"
console.log(typeof null);       // "object" (历史遗留问题)
console.log(typeof {});         // "object"
console.log(typeof []);         // "object"
console.log(typeof function(){});// "function"

2. 特点与局限

  1. 对于原始类型,除了 null 外都能正确返回
  2. 对于引用类型,除了函数外都返回 "object",函数会返回function
  3. 无法区分数组、普通对象等具体对象类型
  4. typeof null 返回 "object" 是历史遗留 bug,原因是在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而null 表示为全零,所以将它错误的判断为 object 。

3. 适用场景

二、instanceof 操作符

1. 基本用法

instanceof 用于检测构造函数的 prototype 属性是否出现在对象的原型链上。所以判断对象数据类型用instanceof比较好。

console.log([] instanceof Array);      // true
console.log({} instanceof Object);     // true
console.log(function(){} instanceof Function); // true

function Person() {}
const p = new Person();
console.log(p instanceof Person);      // true

var str1 = 'hello world'
str1 instanceof String // false 

var str2 = new String('hello world') 
str2 instanceof String // true

2. instanceof可以判断简单的数据类型么?

能的,兄弟能的,在ES6新增的Symbol.hasInstance方法中,允许自定义 instanceof 操作符的行为。如果不是很了解这个可以在MDN中看Symbol.hasInstance - JavaScript | MDN,下面这种方法就可以实现:

class PrimitiveNumber {
    static [Symbol.hasInstance](x) {
        return type x === 'number'
    }
}

console.log(123 instanceof PrimitiverNumber)  // true

这里面就是将instanceof方法重新定义了一下,里面用了type 来判断,所以可以判断基本数据类型number,当然其他的也可以判断,看自己的定义和选择。比如下面的自定义方法:

class MyArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}

console.log([] instanceof MyArray);  // true

3. 手撕 instanceof

主要是使用了Object.getPrototypeOf()方法,它能够返回指定对象的原型对象

function myInstanceof(left, right) {
  // 基本类型直接返回 false
  if (typeof left !== 'object' || left === null) return false;
  // Object.getPrototypeOf是Object自带的一个方法,可以拿到参数的原型对象
  let proto = Object.getPrototypeOf(left);
  // 获取原型对象
  const prototype = right.prototype;
  
  // 无限循环
  while (true) {
    // 如果到顶了还没有找到就返回false
    if (proto === null) return false;
    // 找到了返回true
    if (proto === prototype) return true;
    // 根据原型链一直往上找
    proto = Object.getPrototypeOf(proto);
  }
}

// 测试
console.log(myInstanceof([], Array));   // true
console.log(myInstanceof({}, Object));  // true

console.log(myInstanceof("123", String)); //false 
console.log(myInstanceof(new String("123"), String));//true

4. 特点与局限

  1. 可以检测自定义对象类型
  2. 对于基本数据类型无效(ES6 的 Symbol.hasInstance 可以改变这一行为)
  3. 跨窗口/iframe 检测时会失效,因为构造函数不同
  4. 原型链可能被修改导致检测结果不准确

5. 适用场景

三、Object.prototype.toString

1. 基本用法

调用 Object.prototype.toString() 方法可以返回 [object Type] 格式的字符串。

console.log(Object.prototype.toString.call(42));      // [object Number]
console.log(Object.prototype.toString.call('str'));   // [object String]
console.log(Object.prototype.toString.call(true));    // [object Boolean]
console.log(Object.prototype.toString.call(null));    // [object Null]
console.log(Object.prototype.toString.call(undefined));// [object Undefined]
console.log(Object.prototype.toString.call([]));      // [object Array]
console.log(Object.prototype.toString.call({}));      // [object Object]
console.log(Object.prototype.toString.call(new Date())); // [object Date]

2. 封装通用类型检测函数

function getType(obj) {
  return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

console.log(getType([]));    // 'array'
console.log(getType(null));  // 'null'
console.log(getType(new Map())); // 'map'

代码解析

a. Object.prototype.toString.call(obj)是整个检测机制的核心部分:

这样调用会返回格式为 [object Type] 的字符串,其中 Type 是对象的内部类型。

b. .slice(8, -1)部分从返回的字符串中提取类型名称:

c. .toLowerCase()将提取的类型字符串转换为小写:

这一步不是必须的,但可以使返回结果更统一,方便后续比较。

3. 它能不能判断具体的对象类型?

在ES6中新增的Symbol.toStringTag中允许自定义 Object.prototype.toString 的返回值。

class MyClass {
  get [Symbol.toStringTag]() {
    return 'MyClass';
  }
}

console.log(Object.prototype.toString.call(new MyClass())); // [object MyClass]

4. 特点与局限

  1. 覆盖所有类型:可以检测所有 JavaScript 内置类型,包括基本类型和引用类型
  2. 不受原型链影响:直接调用 Object 的原生方法,不会被对象重写的 toString 方法影响
  3. 一致性:不同环境、不同窗口/iframe 中行为一致
  4. 精确性:能准确区分 Array、Date、RegExp 等特殊对象类型
  5. 返回值:对于自定义对象,默认返回 [object Object]
  6. 自定义:可以通过 Symbol.toStringTag 自定义标签
  7. 缺陷:这种方法在性能方面比typeinstanceof稍慢,但通常差异可以忽略

5. 适用场景

四、constructor 属性

1. 基本用法

通过访问对象的 constructor 属性可以获取其构造函数。

console.log([].constructor === Array);      // true
console.log({}.constructor === Object);     // true
console.log((123).constructor === Number);  // true

function Person() {}
console.log(new Person().constructor === Person); // true

2. 特点与局限

  1. 可以检测基本类型和引用类型的构造函数
  2. constructor 属性容易被修改
  3. null 和 undefined 没有 constructor 属性
  4. 跨窗口/iframe 检测时会失效

3. 适用场景

五、Array.isArray

1. 基本用法

专门用于检测数组类型。

console.log(Array.isArray([]));      // true
console.log(Array.isArray({}));      // false

2. 特点与局限

  1. 专门用于数组检测,比 instanceof 更可靠
  2. 解决了跨窗口/iframe 的数组检测问题
  3. 只能用于数组检测

3. 适用场景

六、其他专用检测方法

1. Number.isNaN

与全局的 isNaN 不同,Number.isNaN 只在值为 NaN 时返回 true。

console.log(Number.isNaN(NaN));      // true
console.log(Number.isNaN('str'));    // false
console.log(isNaN('str'));           // true (全局 isNaN 会先尝试转换为数字)

2. Number.isFinite

检测值是否为有限数字。

console.log(Number.isFinite(123));    // true
console.log(Number.isFinite(Infinity)); // false

七、特殊数据类型检测

1. 检测 NaN

由于 NaN 是 JavaScript 中唯一不等于自身的值,可以利用这一特性检测:

function isNaN(value) {
  return value !== value;
}

// 或者使用 Number.isNaN
console.log(Number.isNaN(NaN));  // true

2. 检测 null 或 undefined

function isNull(value) {
  return value === null;
}

function isUndefined(value) {
  return value === undefined;
}

// 检测 null 或 undefined
function isNil(value) {
  return value == null;  // == 下 null 和 undefined 相等
}

3. 检测原始包装对象

有时候需要区分原始值和其包装对象:

function isPrimitiveWrapper(obj) {
  return (
    obj instanceof Number ||
    obj instanceof String ||
    obj instanceof Boolean
  );
}

八、Object.is和===的区别?

Object.is 和 === (严格相等运算符) 都是 JavaScript 中用于比较两个值是否相等的操作,但它们在处理某些特殊情况时有所不同。

相同点

  1. 都不会进行类型转换
  2. 对于大多数情况,两者的行为是一致的

主要区别

1. 对 NaN 的处理

NaN === NaN  // false
Object.is(NaN, NaN)  // true

Object.is 认为两个 NaN 是相等的,而 === 认为它们不相等。

2. 对 +0 和 -0 的处理

+0 === -0  // true
Object.is(+0, -0)  // false

Object.is 区分 +0 和 -0,而 === 不区分。

3. 其他情况的比较

比较情况=== 结果Object.is 结果
undefined, undefinedtruetrue
null, nulltruetrue
true, truetruetrue
false, falsetruetrue
'foo', 'foo'truetrue
{}, {}falsefalse
NaN, NaNfalsetrue
+0, -0truefalse
5, 5truetrue
5, '5'falsefalse

4. 何时使用

使用 === 作为日常比较的默认选择(更符合直觉,性能略优)

使用 Object.is 当需要:

5. 实现原理

Object.is 的实现可以理解为:

function is(x, y) {
  // 处理 +0 和 -0 的情况
  if (x === 0 && y === 0) {
    return 1 / x === 1 / y;
  }
  // 处理 NaN 的情况
  if (x !== x) {
    return y !== y;
  }
  // 其他情况使用严格相等
  return x === y;
}

6. 总结

Object.is 提供了比 === 更精确的相等性判断,特别是在处理 JavaScript 中的特殊数值(NaN、+0/-0)时。在日常开发中,=== 仍然是首选,但在需要精确比较这些特殊值时,Object.is 是更好的选择。

九、综合比较

方法基本类型引用类型自定义类型跨窗口备注
typeof部分有限null 返回 "object"
instanceof检查原型链
Object.prototype.toString有限最全面
constructor属性易被修改
Array.isArray仅数组专门用于数组检测

十、实用工具函数

1. 通用类型检测函数

function getType(value) {
  // 处理 null 和 undefined
  if (value == null) {
    return value === undefined ? 'undefined' : 'null';
  }
  
  // 处理其他类型
  const type = typeof value;
  if (type !== 'object') return type;
  
  // 处理对象类型
  const toString = Object.prototype.toString.call(value);
  return toString.slice(8, -1).toLowerCase();
}

// 测试
console.log(getType(123));          // 'number'
console.log(getType('str'));        // 'string'
console.log(getType(true));         // 'boolean'
console.log(getType(null));        // 'null'
console.log(getType(undefined));   // 'undefined'
console.log(getType([]));          // 'array'
console.log(getType({}));          // 'object'
console.log(getType(new Date()));  // 'date'
console.log(getType(/regex/));     // 'regexp'
console.log(getType(new Map()));   // 'map'

2. 类型判断辅助函数

const is = {
  // 基本类型
  null: value => value === null,
  undefined: value => value === undefined,
  nil: value => value == null,
  boolean: value => typeof value === 'boolean',
  number: value => typeof value === 'number' && !Number.isNaN(value),
  string: value => typeof value === 'string',
  symbol: value => typeof value === 'symbol',
  bigint: value => typeof value === 'bigint',
  
  // 引用类型
  object: value => value !== null && typeof value === 'object',
  array: value => Array.isArray(value),
  function: value => typeof value === 'function',
  date: value => value instanceof Date,
  regexp: value => value instanceof RegExp,
  promise: value => value instanceof Promise,
  set: value => value instanceof Set,
  map: value => value instanceof Map,
  weakset: value => value instanceof WeakSet,
  weakmap: value => value instanceof WeakMap,
  
  // 特殊值
  nan: value => Number.isNaN(value),
  finite: value => Number.isFinite(value),
  primitive: value => Object(value) !== value,
  
  // 自定义类型
  instance: (value, constructor) => {
    if (typeof constructor !== 'function') return false;
    if (typeof value !== 'object' || value === null) return false;
    return value instanceof constructor;
  }
};

// 测试
console.log(is.array([]));  // true
console.log(is.object({})); // true
console.log(is.null(null)); // true
console.log(is.instance(new Date(), Date)); // true

结语

JavaScript 提供了丰富的数据类型检测方法,每种方法都有其适用场景。在实际开发中:

  1. 对于基本类型检测,typeof 是最简单直接的方式
  2. 需要检测对象具体类型时,优先使用 Object.prototype.toString
  3. 检测数组使用 Array.isArray
  4. 检测自定义对象实例使用 instanceof 或 constructor
  5. 对于复杂场景,可以组合使用多种方法或封装工具函数

理解这些检测方法背后的原理和差异,能够帮助我们在不同场景下选择最合适的检测方式,写出更健壮的 JavaScript 代码。

以上就是JavaScript数据检测方法的全面指南的详细内容,更多关于JavaScript数据检测方法的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文