JavaScript中的类型检查


Posted in Javascript onFebruary 03, 2020

JS 的动态类型有好有坏。好的一面,不必指明变量的类型。不好的是,咱们永远无法确定变量的类型。

typeof运算符可以确定 JS 中的6种类型:

typeof 10;    // => 'number'
typeof 'Hello';  // => 'string'
typeof false;   // => 'boolean'
typeof { a: 1 }; // => 'object'
typeof undefined; // => 'undefined'
typeof Symbol(); // => 'symbol'

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

class Cat { }
const myCat = new Cat();

myCat instanceof Cat; // => true

但是typeof和instanceof的一些行为可能会令人混淆。防范于未然,咱们需要提前了解一些边缘情况。

1. typeof null

typeof myObject === 'object'会告知myObject是否是一个对象类型。举个例子:

const person = { name: '前端小智' };

typeof person; // => 'object'

typeof person是'object',因为person是一个普通的 JS 对象。

在某场景下,变量值可能需要指定为 null,下面是一些场景:

可以使用null来跳过指示配置对象
使用null初始化稍后要保存对象的变量
当函数由于某种原因无法构造对象时,返回null
例如,如果不存在正则表达式匹配项,则str.match(regExp)方法返回null:

const message = 'Hello';
message.match(/Hi/); // => null

这里引出一个问题,可以使用typeof 来区分有值的对象和具有 null 值的对象吗?

let myObject = null;
typeof myObject; // => 'object'

myObject = { prop: 'Value' };
typeof myObject; // => 'object'

从上面可以看出,typeof 对象有值的对象和具有 null 值的对象,得到的结果都是'object'。

可以如下面方法来检测变量是否有对象且不是null:

function isObject(value) {
 return typeof value === 'object' && value !== null;
}

isObject({});  // => true
isObject(null); // => false

除了检查value是否为object: typeof value === 'object'之外,还需要明确地验证null: value !== null。

2. typeof array

如果试图检测一个变量是否包含一个数组,常见的错误就是使用typeof操作符:

const colors = ['white', 'blue', 'red'];

typeof colors; // => 'object'

检测数组的正确方法是使用Array.isArray():

const colors = ['white', 'blue', 'red'];
const hero = { name: 'Batman' };

Array.isArray(colors); // => true
Array.isArray(hero);  // => false

Array.isArray(colors)返回一个布尔值true,表示colors是一个数组。

3.虚值类型检查

JS中的undefined是一个特殊值,表示未初始化的变量。

如果试图访问未初始化的变量、不存在的对象属性,则获取到的值为 undefined :

let city;
let hero = { name: '前端小智', villain: false };

city;   // => undefined
hero.age; // => undefined

访问未初始化的变量 city 和不存在的属性hero.age的结果为undefined。

要检查属性是否存在,可以在条件中使用object[propName],这种遇到值为虚值或者undefined是不可靠的:

function getProp(object, propName, def) {
 // 错误方式
 if (!object[propName]) {
  return def;
 }
 return object[propName];
}

const hero = { name: '前端小智', villain: false };

getProp(hero, 'villain', true); // => true
hero.villain;          // => false

如果对象中不存在propName,则object [propName]的值为undefined。 if (!object[propName]) { return def }保护缺少的属性。

hero.villain属性存在且值为false。 但是,该函数在访问villan值时错误地返回true:getProp(hero, 'villain', true)

undefined是一个虚值,同样false、0和''和null。

不要使用虚值作为类型检查,而是要明确验证属性是否存在于对象中:

typeof object[propName] === 'undefined'
propName in object
object.hasOwnProperty(propName)

接着,咱们来改进getProp()函数:

function getProp(object, propName, def) {
 // Better
 if (!(propName in object)) {
  return def;
 }
 return object[propName];
}

const hero = { name: '前端小智', villain: false };

getProp(hero, 'villain', true); // => false
hero.villain;          // => false

if (!(propName in object)) { ... }条件正确确定属性是否存在。

逻辑运算符

我认为最好避免使用逻辑运算符||作为默情况,这个容易打断阅读的流程:

const hero = { name: '前端小智', villain: false };

const name = hero.name || 'Unknown';
name;   // => '前端小智'
hero.name; // => '前端小智'

// 不好方式
const villain = hero.villain || true;
villain;   // => true
hero.villain; // => false

hero 对象存在属性villain,值为 false,但是表达式hero.villain || true结果为true。

逻辑操作符||用作访问属性的默认情况,当属性存在且具有虚值时,该操作符无法正确工作。

若要在属性不存在时默认设置,更好的选择是使用新的双问号(??)操作符,

const hero = { name: '前端小智', villan: false };

// 好的方式
const villain = hero.villain ?? true;
villain;   // => false
hero.villain; // => false

或使用解构赋值:

const hero = { name: '前端小智', villain: false };

// Good
const { villain = true } = hero;
villain;   // => false
hero.villain; // => false

4. typeof NaN

整数,浮点数,特殊数字(例如Infinity,NaN)的类型均为数字。

typeof 10;    // => 'number'
typeof 1.5;   // => 'number'
typeof NaN;   // => 'number'
typeof Infinity; // => 'number'

NaN是在无法创建数字时创建的特殊数值。NaN是not a number的缩写。

在下列情况下不能创建数字:

Number('oops'); // => NaN

5 * undefined; // => NaN
Math.sqrt(-1); // => NaN

NaN + 10; // => NaN

由于NaN,意味着对数字的操作失败,因此对数字有效性的检查需要额外的步骤。

下面的isValidNumber()函数也可以防止NaN导致的错误:

function isValidNumber(value) {
 // Good
 return typeof value === 'number' && !isNaN(value);
}

isValidNumber(Number('Z99')); // => false
isValidNumber(5 * undefined); // => false
isValidNumber(undefined);   // => false

isValidNumber(Number('99')); // => true
isValidNumber(5 + 10);    // => true

除了typeof value === 'number'之外,还多验证!isNaN(value)确保万无一失。

5.instanceof 和原型链

JS 中的每个对象都引用一个特殊的函数:对象的构造函数。

object instanceof Constructor是用于检查对象的构造函数的运算符:

const object = {};
object instanceof Object; // => true

const array = [1, 2];
array instanceof Array; // => true

const promise = new Promise(resolve => resolve('OK'));
promise instanceof Promise; // => true

现在,咱们定义一个父类Pet和它的子类Cat:

class Pet {
 constructor(name) {
  this.name;
 }
}

class Cat extends Pet {
 sound = 'Meow';
}

const myCat = new Cat('Scratchy');

现在,尝试确定myCat的实例

myCat instanceof Cat;  // => true
myCat instanceof Pet;  // => true
myCat instanceof Object; // => true

instanceof运算符表示myCat是Cat,Pet甚至Object的实例。

instanceof操作符通过整个原型链搜索对象的构造函数。要准确地检测创建对象的构造函数,需要检测 constructor 属性

myCat.constructor === Cat;  // => true
myCat.constructor === Pet;  // => false
myCat.constructor === Object; // => false

只有myCat.constructor === Cat的计算结果为true,表示 Cat 是 myCat实例的构造函数。

6. 总结

运算符typeof和instanceof 用于类型检查。 它们尽管易于使用,但需要注意一些特殊情况。

需要注意的是:typeof null等于'object'。 要确定变量是否包含非null对象,需要显示指明null:

typeof myObject === 'object' && myObject !== null
检查变量是否包含数组的最佳方法是使用Array.isArray(variable)内置函数。

因为undefined是虚值的,所以我们经常直接在条件句中使用它,但这种做法容易出错。更好的选择是使用prop in object来验证属性是否存在。

使用双问号操作系符号object.prop ?? def 或者 { prop = def } = object 来访问可能丢失的属性。

NaN是一个类型为number的特殊值,它是由对数字的无效操作创建的。为了确保变量有正确的数字,最好使用更详细的验证:!isNaN(number) && typeof number === 'number'。

最后,请记住instanceof通过prototype链搜索实例的构造函数。如果不知道这一点,那么如果使用父类验证子类实例,可能会得到错误的结果。

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。

Javascript 相关文章推荐
动态加载js的几种方法
Oct 23 Javascript
IE8 原生JSON支持
Apr 13 Javascript
json数据与字符串的相互转化示例
Sep 18 Javascript
jquery与prototype框架的详细对比
Nov 21 Javascript
JavaScript设计模式之外观模式实例
Oct 10 Javascript
opencv 识别微信登录验证滑动块位置
Aug 07 Javascript
关于vue v-for循环解决img标签的src动态绑定问题
Sep 18 Javascript
jquery实现弹窗(系统提示框)效果
Dec 10 jQuery
js实现带积分弹球小游戏
Jul 21 Javascript
uniapp实现横向滚动选择日期
Oct 21 Javascript
vue backtop组件的实现完整代码
Apr 07 Vue.js
vue响应式原理与双向数据的深入解析
Jun 04 Vue.js
Vue的Eslint配置文件eslintrc.js说明与规则介绍
Feb 03 #Javascript
压缩Vue.js打包后的体积方法总结(Vue.js打包后体积过大问题)
Feb 03 #Javascript
24个解决实际问题的ES6代码片段(小结)
Feb 02 #Javascript
浅谈vuex为什么不建议在action中修改state
Feb 02 #Javascript
vuex+axios+element-ui实现页面请求loading操作示例
Feb 02 #Javascript
vue实现的封装全局filter并统一管理操作示例
Feb 02 #Javascript
node 版本切换的实现
Feb 02 #Javascript
You might like
php版小黄鸡simsimi聊天机器人接口分享
2014/01/26 PHP
JQuery设置和去除disabled属性的5种方法总结
2013/05/16 Javascript
javascript实现网页端解压并查看zip文件
2015/12/15 Javascript
体验jQuery和AngularJS的不同点及AngularJS的迷人之处
2016/02/02 Javascript
AngularJS实现分页显示数据库信息
2016/07/01 Javascript
Angular 路由route实例代码
2016/07/12 Javascript
关于input全选反选恶心的异常情况
2016/07/24 Javascript
JS设置时间无效问题的解决办法
2017/02/18 Javascript
JS中Safari浏览器中的Date
2017/07/17 Javascript
JavaScript递归算法生成树形菜单
2017/08/15 Javascript
详解使用nvm管理多版本node的方法
2017/08/30 Javascript
微信小程序实现动态设置placeholder提示文字及按钮选中/取消状态的方法
2017/12/14 Javascript
vue2 中二级路由高亮问题及配置方法
2019/06/10 Javascript
学习RxJS之JavaScript框架Cycle.js
2019/06/17 Javascript
JS实现水平移动与垂直移动动画
2019/12/19 Javascript
[02:40]DOTA2英雄基础教程 先知
2013/11/29 DOTA
pymongo实现控制mongodb中数字字段做加法的方法
2015/03/26 Python
python使用socket远程连接错误处理方法
2015/04/29 Python
Python功能键的读取方法
2015/05/28 Python
python读取TXT每行,并存到LIST中的方法
2018/10/26 Python
使用python实现抓取腾讯视频所有电影的爬虫
2019/04/15 Python
Python图像处理PIL各模块详细介绍(推荐)
2019/07/17 Python
基于Python生成个性二维码过程详解
2020/03/05 Python
详解用Python进行时间序列预测的7种方法
2020/03/13 Python
python设置中文界面实例方法
2020/10/27 Python
Anaconda的安装与虚拟环境建立
2020/11/18 Python
python中Pexpect的工作流程实例讲解
2021/03/02 Python
利用CSS3实现文字折纸效果实例代码
2018/07/10 HTML / CSS
Stuart Weitzman美国官网:美国奢华鞋履品牌
2016/08/18 全球购物
台湾网购生鲜第一品牌:i3Fresh爱上新鲜
2017/10/26 全球购物
静态变量和实例变量的区别
2015/07/07 面试题
大学生应聘推荐信范文
2013/11/19 职场文书
开学典礼主持词
2014/03/19 职场文书
新学期标语
2014/06/30 职场文书
农业项目合作意向书
2015/05/08 职场文书
聊一聊python常用的编程模块
2021/05/14 Python