鉴定JavaScript中的数据类型
众所周知,JavaScript是一门弱类型的语言,但是这并不代表JavaScript中没有数据类型。JavaScript中常见的数据类型有string、number、object等等,通常我们使用typeof操作符来判断一个变量值的数据类型;但是由于许多问题的存在,往往出现一些出人意料的坑,或者我们无法得到具体的令人满意的答案,所以我们需要自己实现一些函数,用于鉴定各种数据类型,并且得到的结果要符合我们的常识,underscore就实现了一系列这样的函数。
1.数组(Array)的鉴定
如果我们使用typeof操作符鉴定一个数组,我们得到的结果将不会是字符串"array",而是"object",这使得我们无法精确的判断一个对象是否是数组。
有人觉得使用instanceof操作符可以解决这个问题,因为[] instanceof Array === true
,但是这个操作符也并不是那么的靠谱。因为instanceof操作符默认假定只有一个全局执行环境,当我们的网页中包含多个框架时,就会有多个全局执行环境,可能会导致'[] instanceof Array'的结果返回false。
JavaScript中实现了一个内置函数Array.isArray用于判断某个对象是否是数组,但是由于Array.isArray兼容性(IE 9+/FireFox 4+/Safari 5+/Opera 10.5+/Chrome)存在问题,所以我们不得不考虑到在低级浏览器中的后备方案。
那么如何实现这个后备方案呢?我们知道在任何值上调用Object原生的toString方法,都会返回一个[object NativeConstructorName]格式的字符串。
那么我们可以通过这种方式来判断。当Array.isArray()可靠时,我们使用这个函数,当其不可靠时,我们使用Object原生的toString方法:
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
// nativeIsArray = Array.isArray
// toString = Object.prototype.toString
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
上方代码就是underscore的实现。 在之后的实现方案中,我们可以看到许多类型的鉴定都是通过判断Object.prototype.toString返回的字符串来实现的。
2.对象(Object)的鉴定
我们知道在JavaScript中,不只有var object = {}
或者var object = new Object()
这种对象,还有许多内置的对象,比如Error、Function、Array、Number、Date、RegExp、Math等等。
通过测试,这些内置对象在使用typeof操作符鉴定其类型时,都会返回“object”,但是有一个例外,就是Function对象:
typeof new Date()
//"object"
typeof new RegExp()
//"object"
typeof new Boolean();
//"object"
typeof new Array();
//"object"
typeof new Object();
//"object"
typeof new Error();
//"object"
typeof Math
//"object"
typeof function(){};
//"function"
在我们看来,函数理所应当应该是一个对象,因为函数包含许多的属性并且函数具有prototype原型,比如function.length表示函数定义时具有的形参的个数,函数原型中还包含apply、call、bind等常用方法,所以我们应该把函数看做一个对象。
所以我们在判定一个变量值是否是对象时,我们可以通过typeof variable === "object"
来实现,另外还需要考虑typeof variable === "function"
时的情况。当然我们不希望我们判断的参数是一个空对象(null或者undefined),所以应当排除这种情况:
// Is a given variable an object?
//判断传入的参数是否是一个非空对象。
_.isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
//!!双感叹号的作用等同于Boolean函数。
//之所以需要判断!!obj的值,是因为需要确保传入参数是非空对象。
//!!null===false
};
以上代码就是underscore中的具体实现。
3.内置对象(Function、Date、RegExp等)的鉴定
之前有提到过,使用typeof操作符鉴定内置对象时,除了Function之外都会返回字符串"object"。当我们需要具体鉴定内置对象时,显然typeof操作符不再适合。
因为Array也是内置对象,所以我们可以借鉴之前鉴定Array时所使用的方法——Object.prototype.toString。
我们看underscore是如何实现的:
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
//添加一系列的类型判断函数:比如isArguments、isFunction、isString等等。
//具体的方法是通过判断Object.prototype.toString方法来辨别其类型。
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
_['is' + name] = function(obj) {
//相当于是:
//return Object.prototype.toString.call(obj) === `[object ${name}]`;
return toString.call(obj) === '[object ' + name + ']';
};
});
underscore把所有的内置对象的名称字符串放入一个数组当中,然后使用已经定义的_.each
方法遍历元素进行操作。针对每一个名称,为_
变量添加一个鉴定方法,鉴定方法的实现原理是一样的,就是通过对比Object.prototype.toString
返回的字符串与内置对象的名称来判断。
可以看出ES6新增的Set、WeakSet、Symbol、WeakMap、Map等内置对象同样使用该方法进行鉴定。
4.几个特殊值的鉴定
4.1 NaN的鉴定
我们知道,NaN是一个非常特殊的值,为什么特殊呢?因为其含义为非数值(Not a Number),但是typeof NaN === "number"
,因为NaN是JavaScript中唯一一个与任何值都不相等(包括自身)的值:
alert(NaN === NaN);
//false
针对这些特殊情况,JavaScript定义了一个鉴定函数——isNaN。isNaN在接收到一个参数之后,会先尝试将其转换为数值,如果转换失败,则返回true。
比如:
alert(isNaN('123a'));
//true
alert(isNaN(undefined));
//true
这不符合实际,我们需要鉴定确切的NaN,而不是广义上的非数值就是NaN。所以我们首先要确保传入的参数是number类型,这样就缩小了参数的范围,所有的number之外的类型都会被返回false。确保参数是number类型之后,我们再使用isNaN鉴定就行了。
源码:
// Is the given value `NaN`?
_.isNaN = function(obj) {
return _.isNumber(obj) && isNaN(obj);
};
其实在ES6中引入了Number.isNaN方法用于鉴定NaN,它与全局函数isNaN的区别在于Number.isNaN在鉴定之前不会对参数进行强制转换,这就确保了鉴定的结果满足严格的定义。
其实我个人认为,针对其特殊之处可以用另外一个方法实现鉴定。因为NaN是JavaScript中唯一一个自身不等于自身的值,那么我们通过判断变量是否等于自身来鉴定变量值是否为NaN。
实现方案:
function _.isNaN(value){
return value !== value;
}
我在浏览器控制台中简单的尝试了一下,结果是可行的。不知道为什么underscore中没有采用这种方案,如果有同学知道欢迎给我指出:email:zhongdeming428@163.com
。
4.2 Null以及Undefined的鉴定
虽然null == undefined
,但是null !== undefined
,所以我们通过直接比较就行了。
源码:
_.isNull = function(obj) {
return obj === null;
}; // Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
之所以采用void 0 代替undefined,是因为undefined不是一个保留字或者关键字,这代表着在编程时,我们可以对undefined进行赋值!即是在新版的ES中,undefined已经成为了一个Read-Only属性,但是在局部作用域中,undefined还是可以被赋值:
function v(){
let undefined = 'test';
return undefined;
}
v();
//"test"
所以为了防止被用户重新定义undefined,我们需要找到一个可靠的替代品,因为void操作符后接任何参数都会返回undefined,所以这就成为了一个可靠的替代品。
5.结语
JavaScript中的数据类型,是一个永远也说不完道不尽的话题,因为一不小心我们就会写下一个“定时炸弹”,不定时的扔出一些bug,这也是为什么TypeScript大受欢迎的原因。
这也警示了我在接触变量时要非常小心,做好单元测试,才能防止出现一些不该出现的错误。
最后,学习underscore源码,让我学习到了一些平常难以学习到的知识,学到了一些坑应该如何去处理。接下来继续学习,总结经验!
获取更多underscore源码解读:GitHub
鉴定JavaScript中的数据类型的更多相关文章
- JavaScript 中的数据类型
Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...
- javaScript中的数据类型
一.综述 javaScript中的数据类型分为两类: 简单类型:Boolean,Number,String 引用类型:Object 其他:undefined代表变量没有初始化,null代表引用类型为空 ...
- Javascript中的数据类型之旅
虽然Javascript是弱类型语言,但是,它也有自己的几种数据类型,分别是:Number.String.Boolean.Object.Udefined.Null.其中,Object属于复杂数据类型, ...
- 【译】Javascript中的数据类型
这篇文章通过四种方式获取Javascript中的数据类型:通过隐藏的内置[[Class]]属性:通过typeof运算符:通过instanceof运算符:通过函数Array.isArray().我们也会 ...
- javascript 中检测数据类型的方法
typeof 检测数据类型 javascript 中检测数据类型有好几种,其中最简单的一种是 typeof 方式.typeof 方法返回的结果是一个字符串.typeof 的用法如下: typeof v ...
- JavaScript中基本数据类型之间的转换
在JavaScript中共有六种数据类型,其中有五种是基本数据类型,还有一种则是引用数据类型.五种基本数据类型分别是:Number 数值类型.String 字符串类型.Boolean 布尔类型, nu ...
- JavaScript中基本数据类型和引用数据类型的区别(栈——堆)
JavaScript中基本数据类型和引用数据类型的区别 1.基本数据类型和引用数据类型 ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型. 基本数据类型指的是简单的数据段,引用数据 ...
- 面试说:聊聊JavaScript中的数据类型
前言 请讲下 JavaScript 中的数据类型? 前端面试中,估计大家都被这么问过. 答:Javascript 中的数据类型包括原始类型和引用类型.其中原始类型包括 null.undefined.b ...
- 简单回忆一下JavaScript中的数据类型
说到JavaScript,大家都应该知道,它是一门脚本语言,也是一门弱类型语言,也是一门解析型的语言,同时也是一门动态类型的语言. 很好,至于JavaScript中数据类型.其分为基本数据类型和复杂数 ...
随机推荐
- hibernate 执行存储过程 方法
private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) ...
- vue 坑之 vuex requires a Promise polyfill in this browser
android内嵌H5页面不显示出现这个问题,原因有很多 首先,别急,请看下面的推荐方案: 1.找个Android真机测试下(机型版本为4.4以上),真机联调测试 Android 只需要四个步骤: 1 ...
- 江铖:乳腺癌识别By AI
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云加社区技术沙龙 发表于云+社区专栏 演讲嘉宾:江铖,腾讯觅影高级研究员.多年以来一直从事计算机视觉相关的研究.加入腾讯以后,负责腾讯 ...
- CentOS 7下使用yum安装MySQL5.7
1.卸载 1.1先停掉mysql进程,没有安装过的可以直接跳过 pkill - mysqld rpm -qa|grep -i mysql 1.2用命令 yum -y remove -.el7.x86_ ...
- Xshell关闭导致jar服务终止,使Jar在CentOS后台运行
环境:Xsehll6,CentOS7 在项目文件夹新建一个runjar.sh 在sh中写入(举例说明) nohup java -Dfile.encoding=UTF- -jar fin-mgmt-.j ...
- Scrapy框架学习(二)Scrapy入门
接下来以爬取quote.toscrape.com为例完成一遍Scrapy的抓取流程. 首先创建一个Scrapy项目.打开命令行,输入以下命令: scrapy startproject projectn ...
- .netCore2.0 WebApi 传递form表单
随着it的技术发展,目前越来越多的项目采用前后端分离的开发模式,通过webapi提供接口数据来进行交互 最近项目用的是.netCore WebApi,在最近的项目使用中发现一些问题,进行记录.个人简介 ...
- request方法总结
1.获得指定的头 String header = response.getHeader("user-agent"); 2.获得所有头的名称 Enumeration<Stri ...
- Linux终端和win32控制台文本颜色输出
在使用putty.secureCRT.XShell等终端仿真器连接linux系统时,ls.vim等工具的输出都含有各种颜色,这些颜色的输出大大地增强了文本的可读性. 通常我们可以使用echo命令加-e ...
- SASS和SCSS标签详解与scoped局部和全局的使用
首先,学会使用sass: 1.先下载和安装node-sass和一些加载器 $ cnpm install sass-loader node-sass vue-style-loader --D 2.配置w ...