Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript 深入系列之数据类型检测 #94

Open
yuanyuanbyte opened this issue Nov 23, 2021 · 0 comments
Open

JavaScript 深入系列之数据类型检测 #94

yuanyuanbyte opened this issue Nov 23, 2021 · 0 comments

Comments

@yuanyuanbyte
Copy link
Owner

yuanyuanbyte commented Nov 23, 2021

本系列的主题是 JavaScript 深入系列,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

一文搞定 JavaScript 的数据类型检测

1. typeof

对于原始数据类型,我们可以使用 typeof 操作符来判断他的数据类型:

console.log(typeof "");
console.log(typeof 1);
console.log(typeof true);
console.log(typeof null);
console.log(typeof undefined);
console.log(typeof []);
console.log(typeof function(){});
console.log(typeof {});

看看控制台输出什么

可以看到,typeof 对于基本数据类型判断是没有问题的,但是遇到引用数据类型(如:Array)是不起作用的。

typeof null

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

在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"。

曾有一个 ECMAScript 的修复提案(通过选择性加入的方式),但被拒绝了。该提案会导致 typeof null === 'null'。

2. instanceof

typeof 操作符对于原始类型的判断还差强人意,但他是没法用来区分引用数据类型的,因为所有的引用数据类型都会返回"object"。于是 JavaScript 引入了 Java 中使用的 instanceof,用来判断一个变量是否是某个对象的实例,所以对于引用类型我们使用 instanceof 来进行类型判断。

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

用法:

object instanceof constructor

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

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

console.log("1" instanceof String);
console.log(1 instanceof Number);
console.log(true instanceof Boolean);
// console.log(null instanceof Null);
// console.log(undefined instanceof Undefined);
console.log([] instanceof Array);
console.log(function(){} instanceof Function);
console.log({} instanceof Object);

暂且不考虑 null 和 undefined(这两个比较特殊),看看控制台输出什么。

可以看到前三个都是以对象字面量创建的基本数据类型,但是却不是所属类的实例,这个就有点怪了。后面三个是引用数据类型,可以得到正确的结果。如果我们通过 new 关键字去创建基本数据类型,你会发现,这时就会输出 true,如下:

具体为什么会这样呢?我们看 MDN 的解释:

var simpleStr = "This is a simple string";
var newStr    = new String("String created with constructor");

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

接下再来说说为什么 null 和 undefined 为什么比较特殊,实际上按理来说,null 的所属类就是 Null,undefined 就是 Undefined,但事实并非如此:控制台输出如下结果:

浏览器压根不认识这两货,直接报错。在第一个例子你可能已经发现了,typeof null 的结果是 object,typeof undefined 的结果是 undefined

尤其是 null,其实这是 js 设计的一个败笔,早期准备更改 null 的类型为 null,由于当时已经有大量网站使用了 null,如果更改,将导致很多网站的逻辑出现漏洞问题,就没有更改过来,于是一直遗留到现在。具体为什么 typeof null = ‘object’,我们前文已经介绍过了,作为学习者,我们只需要记住就好。

3. constructor

constructor 属性返回 Object 的构造函数。

就是返回对象相对应的构造函数。从定义上来说跟 instanceof 不太一致,但效果都是一样的。

如: (a instanceof Array) // a 是否 Array 的实例?true or false

(a.constructor == Array) // a 实例的构造函数是否为 Array? true or false

console.log(("1").constructor === String);
console.log((1).constructor === Number);
console.log((true).constructor === Boolean);
// console.log((null).constructor === Null);
// console.log((undefined).constructor === Undefined);
console.log(([]).constructor === Array);
console.log((function() {}).constructor === Function);
console.log(({}).constructor === Object);

(这里依然抛开 null 和 undefined)乍一看,constructor 似乎完全可以应对基本数据类型和引用数据类型,都能检测出数据类型,事实上并不是如此,来看看为什么:

function Fn() {};
Fn.prototype = new Array();
var f = new Fn();
console.log(f.constructor === Fn);
console.log(f.constructor === Array);

我声明了一个构造函数,并且把他的原型指向了 Array 的原型,所以这种情况下,constructor 也显得力不从心了。

看到这里,是不是觉得绝望了。没关系,终极解决办法就是第四种办法,看过 jQuery 源码的人都知道,jQuery 实际上就是采用这个方法进行数据类型检测的。

4. Object.prototype.toString.call()

function type(obj) {
    return Object.prototype.toString.call(obj);
}

console.log(type("aaa"));
console.log(type(1));
console.log(type(true));
console.log(type(null));
console.log(type(undefined));
console.log(type([]));
console.log(type(function () {}));
console.log(type({}));

可以看到,所有的数据类型,这个办法都可以判断出来。那就有人质疑了,假如我把他的原型改动一下呢?如你所愿,我们看一下:

可以看到,依然可以得到正确的结果。

总结

  • typeof 操作符,用来检测原始数据类型,但只能检测基本数据类型,没法区分引用数据类型,检测对象,数组和 null 都会返回 object,undefined 会返回 undefined,函数会返回 function。
  • instanceof 运算符,object instanceof constructor 用来检测构造函数的原型是否在参数 object 的原型链上。可以正常检测数组,对象和函数,不能检测对象字面量创建的基本数据类型,如字符串,数组和布尔值,通过 new 关键字创建的基本数据类型才能正常检测。也不能检测 null 和 undefined。
  • constructor 属性,返回对象的构造函数。可以检测基本数据类型和引用数据类型,但改变原型指向后,该属性会跟着变,所以用来检测数据类型不严谨。不能检测 null 和 undefined。
  • Object.prototype.toString.call() 可以检测所有数据类型,包括 null 和 undefined,并且不会像 constructor 那样随着对象原型指向的改变而改变。

参考

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant