【JavaScript】js中判断变量类型的几种方式

简言

在JavaScript中有好几种判断变量类型的方式,此篇主要探讨判断变量类型的几种方式和特点。

判断类的方式

typeof

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

typeof operand

参数:

  • operand表示要返回类型的对象或基本类型的表达式。

特点:适合判断常见的基本类型,引用类型需要做兼容处理才能准确区分

描述 结果
Undefined “undefined”
Null “object”
Boolean “boolean”
Number “number”
BigInt “bigint”
String “string”
Symbol “symbol”
Function;classes也是函数) “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 会尝试把参数解析成数值
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";

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
可以理解为:某个实例对象(变量)来自于某个构造函数,则返回true,否则返回false。

object instanceof constructor

参数:

  • object — 某个实例对象
  • constructor — 某个构造函数。

特点:如果变量值是用new创建的,也可以使用它判断类型。

var simpleStr = "This is a simple string";
var myString = new String();
var newStr = new String("String created with constructor");
var myDate = new Date();
var myObj = {};
var myNonObj = Object.create(null);

simpleStr instanceof String; // 返回 false,非对象实例,因此返回 false
myString instanceof String; // 返回 true
newStr instanceof String; // 返回 true
myString instanceof Object; // 返回 true

myObj instanceof Object; // 返回 true,尽管原型没有定义
({}) instanceof Object; // 返回 true,同上
myNonObj instanceof Object; // 返回 false,一种创建非 Object 实例的对象的方法

myString instanceof Date; //返回 false

myDate instanceof Date; // 返回 true
myDate instanceof Object; // 返回 true
myDate instanceof String; // 返回 false

Object.prototype.constructor

Object 实例的 constructor 数据属性返回一个引用,指向创建该实例对象的构造函数。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。
除了 null 原型对象之外,任何对象都会在其 [[Prototype]] 上有一个 constructor 属性。使用字面量创建的对象也会有一个指向该对象构造函数类型的 constructor 属性,例如,数组字面量创建的 Array 对象和对象字面量创建的普通对象。
特点:不能判断null,且非基本类型判断可能会不准确。

非基本类型对象的 constructor 属性可以更改

const o1 = {};
o1.constructor === Object; // true

const o2 = new Object();
o2.constructor === Object; // true

const a1 = [];
a1.constructor === Array; // true

const a2 = new Array();
a2.constructor === Array; // true

const n = 3;
n.constructor === Number; // true

const arr = [];
arr.constructor = String;
arr.constructor === String; // true
arr instanceof String; // false
arr instanceof Array; // true

const foo = new Foo();
foo.constructor = "bar";
foo.constructor === "bar"; // true

Object.prototype.toString

所有继承自 Object.prototype 的对象(即,除了 null-prototype 对象之外的对象)都继承 toString() 方法。
要将基本的 Object.prototype.toString() 用于重写的对象(或者在 null 或 undefined 上调用它),你需要在它上面调用 Function.prototype.call() 或者 Function.prototype.apply(),将要检查的对象作为第一个参数传递(称为 thisArg)。

Function.prototype.call()和Function.prototype.apply() 结果相同

特点:基本类型和非基本类型都可以判断,不过不可靠,因为对象可以通过定义 Symbol.toStringTag 属性来更改 Object.prototype.toString() 的行为,从而导致意想不到的结果,Object.prototype.toString也可以更改

const arr = [1, 2, 3];

arr.toString(); // "1,2,3"
Object.prototype.toString.call(arr); // "[object Array]"

const toString = Object.prototype.toString;

toString.call(new Date()); // [object Date]
toString.call(new String()); // [object String]
// Math has its Symbol.toStringTag
toString.call(Math); // [object Math]

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

通过定义 Symbol.toStringTag 属性来更改 Object.prototype.toString() 的行为:

const myDate = new Date();
Object.prototype.toString.call(myDate); // [object Date]

myDate[Symbol.toStringTag] = "myDate";
Object.prototype.toString.call(myDate); // [object myDate]

Date.prototype[Symbol.toStringTag] = "prototype polluted";
Object.prototype.toString.call(new Date()); // [object prototype polluted]

Object.prototype.toString直接更改将会使Object.prototype.toString.call报错

优化的类型判断方法

下面是mdn上的优化方法,你可以基于该方法进行扩展。

function type(value) {
  if (value === null) {
    return "null";
  }
  const baseType = typeof value;
  // 基本类型
  if (!["object", "function"].includes(baseType)) {
    return baseType;
  }

  // Symbol.toStringTag 通常指定对象类的“display name”
  // 它在 Object.prototype.toString() 中使用。
  const tag = value[Symbol.toStringTag];
  if (typeof tag === "string") {
    return tag;
  }

  // 如果它是一个函数,其源代码以 "class" 关键字开头
  if (
    baseType === "function" &&
    Function.prototype.toString.call(value).startsWith("class")
  ) {
    return "class";
  }

  // 构造函数的名称;例如 `Array`、`GeneratorFunction`、`Number`、`String`、`Boolean` 或 `MyCustomClass`
  const className = value.constructor.name;
  if (typeof className === "string" && className !== "") {
    return className;
  }

  // 在这一点上,没有合适的方法来获取值的类型,因此我们使用基本实现。
  return baseType;
}

你可能感兴趣的:(JavaScript,javascript,开发语言,ecmascript)