Skip to content

在 JavaScript 中判断类型有哪几种方式 #68

@wangjing013

Description

@wangjing013

在讲解类型判断之前先来看看类型的分类,通常把类型分为: 基础类型引用类型

基础类型包括如下:

  • String
  • Boolean
  • Number
  • Symbol
  • Null
  • Undefined

常见的引用类型包括:

  • Object
  • Array
  • Function

知道有那些类型之后,接下我们来看看 JS 中提供那些常见的类型判断方式:

typeof

返回操作数的类型。

下面通过使用 typeof 来对基本数据类型、引用类型进行操作。

const num = 1;
const str = '1';
const bool = true;
const undefinedVar = undefined;
const nullVar = null;

const obj = {};
const arr = [];
const fn = function(){}

console.log(typeof num); // "number"
console.log(typeof str); // "string"
console.log(typeof bool); // "boolean"
console.log(typeof undefinedVar); // "undefined"
console.log(typeof nullVar); // "object"

console.log(typeof obj); // "object"
console.log(typeof arr); // "object"
console.log(typeof fn);  // "function"

从上面输出结果来看 typeof 适用于基础类型。可能有人会疑惑为什么 typeof null 其返回是 ''object''

主要JavaScript 实现上的 bug。 这里不在累赘了,感兴趣可以查阅这篇文章: typeof_null

instanceof

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

从定义上来看,它只适用于引用数据类型。

const obj = {};
const arr = [];
const fn = function(){}

console.log(obj instanceof Object); // true
console.log(arr instanceof Array); //  true
console.log(fn instanceof Function); // true

从上面的例子中可以看到,通过 instanceof 基本上能确定引用数据的类型。

instanceof 能满足所有使用场景 ?不能

下面来看个具体例子:

  <!--parent.html-->
  <iframe src="./child.html"> </iframe>
  <script>
    window.name = "wangjing";
    function callback(arr) {
      console.log(arr instanceof Array); // false
      console.log(Object.prototype.toString.call(arr)); // [object Array]
      console.log(Array.isArray(arr)); // true
    }
  </script>
  <!--child.html-->
  <script>
    window.onload(function(){
      typeof window.parent.callback === 'function' && window.parent.callback([]);
    });
  </script>

上面创建两个页面分别是parent.htmlchild.html,其中 child 通过 window.parent 方式调用 parent 页面提供的 callback 函数,并传递一个数组对象。

从输出来看 arr instanceof Arrray 返回 false。为什么?因为在不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。

聪明的你肯定会思考,为什么Object.prototype.toString.call(arr) 返回值为 [object Array]? 要理解它其实并不难,只需知道 Object.prototype.toString 它返回内容表示的是什么。

下面是摘取来自 MDN 中定义:

返回一个表示该对象的字符串.

再来看看 ECMA规范 15.2.4.2 Object.prototype.toString:

When the toString method is called, the following steps are taken:

  • If the this value is undefined, return "[object Undefined]".
  • If the this value is null, return "[object Null]".
  • Let O be the result of calling ToObject passing the this value as the argument.
  • Let class be the value of the [[Class]] internal property of O.
  • Return the String value that is the result of concatenating the three Strings "[object ", class, and "]".

toString 方法被调用时,将会执行如下步骤:

  • 如果当前的值为 undefined, 返回 [object Undefined]
  • 如果当前的值为 null, 返回 [object Null]
  • O 是调用 ToObject 传递此值作为参数的结果。
  • classO[[Class]]] 内部属性的值。
  • 返回 String 值,该值由 ''[object'',class 和 "]" 三个字符串拼接而成。

在上面的处理过程中,最终的目的拿到对应的 class 值,而它对应的是对象内置属性 [[Class]] 上,那 [[Class]]是什么?

内部属性 类型 描述
[[Class]] 字符串 一个字符串,它是对象的分类标识。

分类标识其实就是对象的构造函数名称。

现思考下可以修改对象的 [[Class]]值?不能。

因为在 ECMA 规范并没有提供其它方法去修改 [[Class]] 属性(也就是只读,确定它可以作为类型判断的标准)。同时它的值只能通过 Object.prototype.toString 去访问。

基于上面相信大家了解 Object.prototype.toString 基于什么获取内置和宿主对象的类型。

接下来看下 isArray,依然可以参考 ECMA规范 中对 isArray 的定义。

The isArray function takes one argument arg, and returns the Boolean value true if the argument is an object whose class internal property is "Array"; otherwise it returns false. The following steps are taken:

  • If Type(arg) is not Object, return false.
  • If the value of the [[Class]] internal property of arg is "Array", then return true.
  • Return false.

isArray 函数接收一个参数,如果传入的参数是一个对象且内部属性[[Class]]值为"Array" 则返回 true,否则返回 false

isArray 被调用时,将会执行如下步骤:

  • 如果 arg 不是一个对象,返回 false
  • 如果当前值内部属性 [[Class]] 值为 Array 时,返回 true
  • 其它返回 false

仔细看 toStringisArray 都是根据对象的标识([[Class]])来确定具体的类型。这就是它们不受不同上下文影响的原因。

最佳实战

  • typeof 适用于基本数据类型。
  • instanceof 适用于引用类型,但是它不能满足所有的场景,有时需要另外的一种方式来弥补toString
  • toString 只能应用于宿主对象、内部对象,对于用于自定义的对象,没办法很好区分。

具体使用那种方式,可以根据具体业务场景来。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions