众所周知,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中的数据类型的更多相关文章

  1. JavaScript 中的数据类型

    Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...

  2. javaScript中的数据类型

    一.综述 javaScript中的数据类型分为两类: 简单类型:Boolean,Number,String 引用类型:Object 其他:undefined代表变量没有初始化,null代表引用类型为空 ...

  3. Javascript中的数据类型之旅

    虽然Javascript是弱类型语言,但是,它也有自己的几种数据类型,分别是:Number.String.Boolean.Object.Udefined.Null.其中,Object属于复杂数据类型, ...

  4. 【译】Javascript中的数据类型

    这篇文章通过四种方式获取Javascript中的数据类型:通过隐藏的内置[[Class]]属性:通过typeof运算符:通过instanceof运算符:通过函数Array.isArray().我们也会 ...

  5. javascript 中检测数据类型的方法

    typeof 检测数据类型 javascript 中检测数据类型有好几种,其中最简单的一种是 typeof 方式.typeof 方法返回的结果是一个字符串.typeof 的用法如下: typeof v ...

  6. JavaScript中基本数据类型之间的转换

    在JavaScript中共有六种数据类型,其中有五种是基本数据类型,还有一种则是引用数据类型.五种基本数据类型分别是:Number 数值类型.String 字符串类型.Boolean 布尔类型, nu ...

  7. JavaScript中基本数据类型和引用数据类型的区别(栈——堆)

    JavaScript中基本数据类型和引用数据类型的区别 1.基本数据类型和引用数据类型 ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型. 基本数据类型指的是简单的数据段,引用数据 ...

  8. 面试说:聊聊JavaScript中的数据类型

    前言 请讲下 JavaScript 中的数据类型? 前端面试中,估计大家都被这么问过. 答:Javascript 中的数据类型包括原始类型和引用类型.其中原始类型包括 null.undefined.b ...

  9. 简单回忆一下JavaScript中的数据类型

    说到JavaScript,大家都应该知道,它是一门脚本语言,也是一门弱类型语言,也是一门解析型的语言,同时也是一门动态类型的语言. 很好,至于JavaScript中数据类型.其分为基本数据类型和复杂数 ...

随机推荐

  1. C 标准库 - string.h之strspn使用

    strspn Returns the length of the initial portion of str1 which consists only of characters that are ...

  2. Git命令基本操作备忘

    创建Git仓库并上传到远程Git仓库 git init git config user.name "显示的名称" git config user.email "EMAIL ...

  3. html控件

    checkbox val = "<li class='layer'><label><input type='checkbox' checked name='la ...

  4. Boost application performance using asynchronous I/O-ref

    http://www.ibm.com/developerworks/linux/library/l-async/?S_TACT=105AGX52&S_CMP=cn-a-l Introducti ...

  5. Java入门系列-16-继承

    这一篇文章教给新手学会使用继承,及理解继承的概念.掌握访问修饰符.掌握 final 关键字的用法. 继承 为什么要使用继承 首先我们先看一下这两个类: public class Teacher { p ...

  6. [转]Newtonsoft JSON how to dynamically change the date format?

    本文转自:http://www.howtobuildsoftware.com/index.php/how-do/cg8K/jsonnet-newtonsoft-json-how-to-dynamica ...

  7. windows 下配置ndk环境,无需cygwin

    时隔好久要用ndk编译jni库,本以为配制安装cygwin环境,便按部就班的下载安装,但是公司的网速真的不给力,三天安装了三四次都没有安装成功(我选择的是在线安装),于是我便开始查ndk的官网看看,发 ...

  8. SQL 除去回车符 除去空格符

    update table set fa=replace(fa,chr(13),'') ; --- 除去回车符 update table set fa=replace(fa,' ','') ; --- ...

  9. C++测验代码

    /* 返回字符串前n位和返回整数前n位 */ #include <iostream> unsigned long left(unsigned long num, int n); char ...

  10. 预防XSS方法:HtmlEncode和JavaScriptEncode(转)

    XSS又称CSS,全称Cross SiteScript,跨站脚本攻击,是Web程序中常见的漏洞,XSS属于被动式且用于客户端的攻击方式,所以容易被忽略其危害性.其原理是攻击者向有XSS漏洞的网站中输入 ...