JavaScript中的数据类型分为两类,undefined,number,boolean,string,symbol,bigint,null[1]组成的基础类型Object、Function、Array等类型组成的引用类型



如何判断数据属于哪种类型是JavaScript中非常重要的一个知识点,其中最常用的两个方法就是分别使用typeof与instanceof这两个关键字来对数据的类型进行判断。

typeof与instanceof虽然都可以用来对数据所属的类型进行判断,但是它们之间还是存在差异的,而这种差异主要存在于两个方面:

1.作用点的不同;

typeof主要用来判断基础数据类型,instanceof则是用来判断引用数据类型。

2.底层逻辑的不同;

typeof是根据数据在存储单元中的类型标签来判断数据的类型,instanceof则是根据函数的prototype属性值是否存在于对象的原型链上来判断数据的类型。

typeof判断数据类型共有8个值,它们分别是‘undefined’、‘number’、‘boolean’、‘string’、‘symbol’、‘bigint’、‘object’和‘function’。

使用typeof就可以很好的判断数据类型undefined、number、boolean、string、symbol和bigint。

不过在判断基础类型null时,使用typeof便不再准确了。这个问题的产生可以追溯到JavaScript的第一个版本[2],在这个版本中,单个值在栈中占用32位的存储单元,而这32位的存储单元又可以划分为类型标签(1-3位)和实际数据,类型标签存储于低位中,具体可以分成5种:

1.当第0位、第1位和第2位皆为0时,typeof判断此数据类型为’object’;

2.当第0位为1时,typeof判断此数据类型为’number(整数)’;

3.当第0位与第2位皆为0,而第1位为1时,typeof判断此数据类型为’number(浮点数)’;

4.当第0位与第1位皆为0,而第2位为1时,typeof判断此数据类型为’string’;

5.当第1位与第2位皆为1,而第0位为0时,typeof判断此数据类型为’boolean’;

此外还有两种特殊情况:

undefined:整数−2^30 (整数范围之外的数字)

null:第0位到第31位皆为0

当数据值为null时,正好满足当第0位、第1位和第2位皆为0时,typeof判断类型为’object’的条件,所以typeof null === 'object'的结果为true。

使用typeof判断function也是存在问题的:

在 IE 6, 7 和 8 上,很多宿主对象是对象而不是函数。

例如:

typeof alert === 'object';//true

还有老版本Firefox中的

typeof /[0-9]/ === 'function';//true

像这种的还有很多,就不一样举例了,多半是浏览器实现差异,现在已经统一标准了。

我在ie11上运行的结果:

typeof alert === 'function';//true

在当前最新版Firefox上运行的结果:

typeof reg === 'object';//true

typeof在判断引用类型还存在一些问题,例如:

typeof {} === 'object';//true
typeof [] === 'object';//true
typeof window === 'object';//true
typeof new Map() === 'object';//true

这个时候如果想要知道更详细的信息就需要使用instanceof关键字了。

alert instanceof Function;//true
({}) instanceof Object;//true
([]) instanceof Array;//true
window instanceof Window;//true
(new Map()) instanceof Map;//true

使用instanceof运算符,我们可以清楚的判断对象的原型链上是否存在函数的prototype属性值。

不过instanceof也并不能完全可信,比如通过Symbol.hasInstance属性可以影响instanceof的判断结果:

function Person(){
} Object.defineProperty(Person,Symbol.hasInstance,{
value : function(){
return false;
}
}) let p = new Person(); p instanceof Person;//false

但是Symbol.hasInstance属性并不会影响到数据的原型链,使用自定义的myInstanceof方法[3]不会受到Symbol.hasInstance属性的影响:

/**
* obj 变量
* fn 构造函数
*/
function myInstanceof(obj,fn){
let _prototype = Object.getPrototypeOf(obj);
if(null === _prototype){
return false;
}
let _constructor = _prototype.constructor;
if(_constructor === fn){
return true;
}
return myInstanceof(_prototype,fn);
} function Person(){
} Object.defineProperty(Person,Symbol.hasInstance,{
value : function(){
return false;
}
}) let p = new Person(); p instanceof Person;//false myInstanceof(p,Person);//true

自定义的myInstanceof方法改进版:

/**
* obj 变量
* fn 构造函数
*/
function myInstanceof(obj,fn){
let _prototype = Object.getPrototypeOf(obj);
if(null === _prototype){
return false;
}
let _constructor = _prototype.constructor;
if(_constructor[Symbol.hasInstance]){
return _constructor[Symbol.hasInstance](obj);
}
if(_constructor === fn){
return true;
}
return myInstanceof(_prototype,fn);
} function Person(){
} Object.defineProperty(Person,Symbol.hasInstance,{
value : function(){
return false;
}
}) let p = new Person(); p instanceof Person;//false myInstanceof(p,Person);//false

数据和类型不在一个全局变量下时instanceof也会输出错误的结果

比方说现在定义两个html文件,分别为main.html和iframe.html,代码如下:

main.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>main</title>
<script type="text/javascript">
window.onload = function(){
console.log('document.domain : ' + document.domain);
let iframe = document.documentElement.getElementsByTagName('iframe')[0];
let p = iframe.contentWindow.window.document.documentElement.getElementsByTagName('p')[0];
console.log('p instanceof Object : ' + (p instanceof Object));//p instanceof Object : false
}
</script>
</head>
<body>
<iframe src="./.html"></iframe>
</body>
</html>

iframe.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>iframe</title>
</head>
<body>
<p>1</p>
</body>
</html>

npx http-server打开main.html后,得到结果p instanceof Object : false

造成这种结果原因在于:

iframe.contentWindow.window.Object === window.Object;//false

那浏览器要为什么这样呢?都用一个Object构造函数不好吗?

我们这样看:

main.html打开一个window(我们现在叫它main_window),iframe.html打开一个window(我们现在叫它iframe_window)。

我们现在从iframe_window中获取一个p元素对象,它的原型链为—HTMLParagraphElement.prototype -> HTMLElement.prototype -> Element.prototype -> Node.prototype -> EventTarget.prototype -> Object.prototype。

然后我们在main.html文件中,去判断p instanceof Object,也就是判断 p instanceof main_window.Object。p元素是在iframe.html文件中被构造的,所以p instanceof iframe_window.Object === true。如果想让p instanceof main_window.Object === true

那么要满足iframe_window.Object === main_window.Object。但是这个条件肯定是不能满足的。如果iframe_window.Object === main_window.Object,那么我在iframe.html文件中修改Object函数,就会作用到main.html中,这样会引发很严重的安全的问题,以及一系列莫名其妙的bug。

所以不同的全局变量下的同名构造函数并不是同一个函数,这导致了instanceof在数据与函数位于不同全局变量下时会判断出错。

不过在这个例子使用typeof倒是可以解决问题,只是要记住判断数据是不是null类型:

null !== p && typeof p === 'object';//true

因为typeof判断的是存储单元中的标签类型,所以不会受到影响。

参考


  1. 一般基础类型我是用小写字母开头。

  2. 此时还没有Symbol、BigInt,故不在讨论范围内。

  3. myInstanceof方法的由来

深入了解typeof与instanceof的使用场景及注意事项的更多相关文章

  1. JavaScript中typeof,instanceof,hasOwnProperty,in的用法和区别

    一. typeof操作符 typeof操作符用于返回正在使用值的类型. // 使用原始值 let mNull = null; let mUndefined = undefined; let mStri ...

  2. js中typeof与instanceof用法

    今天写JS代码,遇到动态生成多个名称相同的input复选按钮 需要判断其是否是数组,用到了if (typeof(document.MapCheckMgr.checkid)!="undefin ...

  3. JS中typeof与instanceof的区别

    JavaScript 中 typeof 和 instanceof 常用来判断一个变量是否为空,或者是什么类型的.但它们之间还是有区别的: Typeof typeof 是一个一元运算,放在一个运算数之前 ...

  4. js中typeof和instanceof

    对于typeof和instanceof,我们经常用来检测数据的类型.typeof可以检测Number.Boolean.String.Undefined类型,对于其他类型的数据都返回为object:而i ...

  5. JS typeof与instanceof的区别

    typeof 与 instanceof 通常是用来判断一个变量的类型,二者有如下区别: typeof: 判断一个变量的类型,返回值是字符串形式,返回结果有如下几种: number,boolean,st ...

  6. JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  7. 原生JS:delete、in、typeof、instanceof、void详解

    delete.in.typeof.instanceof.void详解 本文参考MDN做的详细整理,方便大家参考[MDN](https://developer.mozilla.org/zh-CN/doc ...

  8. typeof和instanceof

    JavaScript 中 typeof 和 instanceof 常用来判断一个变量是否为空,或者是什么类型的.但它们之间还是有区别的: typeof typeof 是一个一元运算,放在一个运算数之前 ...

  9. 面向对象的程序设计(二)理解各种方法和属性typeof、instanceof、constructor、prototype、__proto__、isPrototypeOf、hasOwnProperty

    //理解各种方法和属性typeof.instanceof.constructor.prototype.__proto__.isPrototypeOf.hasOwnProperty. //1.typeo ...

随机推荐

  1. Java-Servlet知识总结

    目录 Servlet概述 为什么要学习Servlet 什么是 Servlet 工作流程 生命周期 处理请求的方法 HttpServletRequest 和 HttpServletResponse Ht ...

  2. 宝塔的url计划任务

    to通过url访问 就像访问你的网站一样 然后控制器/方法里面写你要做的操作 就可以了 ,简单的一批

  3. ELK一个优秀的日志收集、搜索、分析的解决方案

    1 什么是ELK? ELK,是Elastaicsearch.Logstash和Kibana三款软件的简称.Elastaicsearch是一个开源的全文搜索引擎.Logstash则是一个开源的数据收集引 ...

  4. Centos7下安装MySQL8.0.23-小白的开始

    首先简单介绍一下什么叫MySQL: 数据库简而言之就是存储数据的仓库,为了方便数据的存储和管理,它将数据按照特定的规律存储在磁盘上.是为了实现一定的目的,按照某种规则组织起来的数据的集合: MySQL ...

  5. Redis 实战 —— 05. Redis 其他命令简介

    发布与订阅 P52 Redis 实现了发布与订阅(publish/subscribe)模式,又称 pub/sub 模式(与设计模式中的观察者模式类似).订阅者负责订阅频道,发送者负责向频道发送二进制字 ...

  6. IGXE搬砖项目

    主要的赚钱方式和倒爷其实是差不多的,自动检测igxe平台上价格与buff相差8.5%以上的饰品,按照历史价格进行一定的过滤,防止翻车,然后自动购买. 2019年经历了十几次的改进以对抗同行的脚本,到1 ...

  7. 2.kafka架构深入——生产者

    一个topic有多个partition,每个partition又有多个副本,在这些副本中又有一个leader和多个follower. 1)分区的原因 (1)方便在集群中扩展,每个Partition可以 ...

  8. Webpack4.0各个击破(10)integration篇

    一. Integration 下文摘自webpack中文网: 首先我们要消除一个常见的误解,webpack是一个模块打包工具(module bundler),它不是一个任务执行工具,任务执行器是用来自 ...

  9. 数位dp 笔记

    目录 数位dp 笔记 解决的问题 & 主体思想 入门 -- windy数 绕一个弯 -- 萌数 the end? -- 恨7不成妻 小心细节 [SDOI2016]储能表 复杂度起飞 [AHOI ...

  10. 济南学校D1T3_hahaha

    [问题描述] 小Q对计算几何有着浓厚的兴趣.他经常对着平面直角坐标系发呆,思考一些有趣的问题.今天,他想到了一个十分有意思的题目: 首先,小Q会在轴正半轴和轴正半轴分别挑选个点.随后,他将轴的点与轴的 ...