在Badoo的时候我们写了大量的JS脚本,光是在我们的移动web客户端上面就有大概60000行,可想而知,维护这么多JS可是相当具有挑战性的。在写如上规模js脚本客户端应用的时候我们必须对一件事保持警惕,就是避免异常的发生。在本篇文章里面,我想谈谈一部分类型检查异常,这时候你或许很难碰到 - 一个TypeError

  在MDN链接里面是这么解释的:

"当传递给操作符或者函数的操作符或者参数与操作符或者函数本身所期望的操作符或函数类型不兼容的时候就会抛出一个TypeError" -MDN

  所以,想要避免有TypeError抛出,我们不仅需要检测下传递给函数的值是否正确,还要在在某操作数上使用操作符之前检查所写的任何有关验证这个操作数的代码。比如,某个操作符不能检测null或undefined,而instanceof又不能检测非函数的参数,在这种情况下把这些操作符使用到某操作数肯定会因为不兼容而抛出TypeError。如果你学过类似Java这样的静态类型编程语言就会发现Javascript在这上面极度的敏感,使得你可能会有去使用DartTypeScript 的“超集Javascript”语言的冲动。如果你已经习惯了写JavaScript或者说JavaScript的编程基础蛮好,你所需要做的就是不要灰心。因为加上类型检测功能并很复杂,更何况可以帮助那些想读懂你代码的人

  那下面就以一个很直接的实例开始解说吧。这个例子主要是从服务器上获取数据,然后在数据上使用操作符后渲染出HTML代码。

Api.get('/conversations', function (conversations) {

    var intros = conversations.map(function (c) {
var name = c.theirName;
var mostRecent = c.messages[0].text.substring(0, 30);
return name + ': ' + mostRecent;
}); App.renderMessages(intros); });
 

  乍眼看这代码时,我们会发现不能确定里面的conversations该是什么类型的数据。既然代码里面有一个很明显需要传一个数组为变量的数据地图函数,那我们还是可以假定下它的类型,不过这种假定类型还不一定是合理的,因为实际上它可以代表任何能实现一个数据地图函数功能的类型。传给数据地图的函数可以给c变量生成许多种假定的类型。如果这些假定类型是错误的,那么就会抛出一个TypeError错误,这样一来,导致renderMessages()函数永远都不可能被调用。

  那么,在本实例中,我们怎么去检测验证类型呢?嗯,首先我们来看下Javascript中各种验证数据类型的函数吧。

  typeof

  typeof 运算符返回一个字符串来表明这个运算对象的类型。但是它返回的类型非常有限,例如以下所返回的全部为 "object"

typeof {};
typeof [];
typeof null;
typeof document.createElement('div');
typeof /abcd/;

  instanceof

  instanceof 运算符是用来确定一个对象的原型链包含原型属性的一个构造函数。

[] instanceof Array; // true

var Foo = function () {};
new Foo() instanceof Foo; // true

  这样使用instanceof去检查一个 native 对象不是一个好主意,因为它并不适用与原始事物的值.

'a' instanceof String; // false
5 instanceof Number; // false
true instanceof Boolean; //false

  Object.prototype.toString

  这个方法在各大JS框架中经常被用于推断数据类型,也正是因为这个方法的使用规范非常简明,它普遍适用于各大浏览器。在 15.2.4.2 这个版本的 ECMA-262 规范中,toString方法是这样定义的:

  • 如果参数是未定义的值,则返回"[object Undefined]".
  • 如果参数为null,则返回"[object Null]".
  • 如果适用ToObject函数传递参数,则返回对象.
  • 如果参数为类,则返回包含对象的类.(Let class be the value of the [[Class]] internal property of O.)
  • 返回一个由"[对象", 类, 和"]"拼接而成的字符串.

  因此,这个方法永远会以 “[Foo 对象]”的方式返回一个字符串,这个Foo有可能是 “空”或是“未定义”又或者是一个用来创建此字符串的类。 使用这个方法将转化一个普通的值、或是一个表达式为任何我们想要的结果,而且这个结果将以字符串的形式呈现。

var type = function (o) {
var s = Object.prototype.toString.call(o);
return s.match(/\[object (.*?)\]/)[1].toLowerCase();
} type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"

  这就是要解决的问题,对吗?很不幸的这还不是。仍然有一些实例中的这个方法能够返回一些不是我们预期的值。

type(NaN); // "number"
type(document.body); // "htmlbodyelement"

  这两个例子返回的值也许并不是我们所预想的那样。在例子NaN中,返回“number”是因为技术上NaN是一种数字类型,虽然几乎我们知道的所有的例子中,如果一个“东西”是数字,就不是非数字。被用来实现<body>元素的内部类是HTMLBodyElement(至少在谷歌和火狐浏览器中如此),每一个元素都有各自的指定类。在大多数应用场景中,我们只想知道如果一个“东西”是否是一个元素,如果我们关心元素的标签名,我们可以使用tagNameproperty来获取。然而,我们能够修改我们现有的方法来处理这些事情。

var type = function (o) {

    // handle null in old IE
if (o === null) {
return 'null';
} // handle DOM elements
if (o && (o.nodeType === 1 || o.nodeType === 9)) {
return 'element';
} var s = Object.prototype.toString.call(o);
var type = s.match(/\[object (.*?)\]/)[1].toLowerCase(); // handle NaN and Infinity
if (type === 'number') {
if (isNaN(o)) {
return 'nan';
}
if (!isFinite(o)) {
return 'infinity';
}
} return type;
}

  现在我们有一个可以对任何我们感兴趣的返回正确类型的方法,我们可以改进原来的例子以确保我们没有任何类型错误。

Api.get('/conversations', function (conversations) {

    // 大家读到这里就知道conversation应该是个数组
if (type(conversations) !== 'array') {
App.renderMessages([]);
return;
} var intros = conversations.map(function (c) { if (type(c) !== 'object') {
return '';
} var name = type(c.theirName) === 'string' ? c.theirName : '';
var mostRecent = ''; if (type(c.messages) === 'array' &&
type(c.messages[0]) === 'object' &&
type(c.messages[0].text) === 'string') {
mostRecent = c.messages[0].text.substring(0, 30);
} return name + ': ' + mostRecent;
}); //在这里可以做的更多
App.renderMessages(intros);
});

  很明显的事实是我们不得不添加更多的代码来阻止出现类型错误的风险,但Badoo只是让我们添加很少的JavaScript代码,这意味着我们的应用更加稳定。最后,很明显type方法每一次检测的时候都要和字串做比较。这很好改进。我们可以构建和Underscore/LoDash/jQuery类似的API:

['Null',
'Undefined',
'Object',
'Array',
'String',
'Number',
'Boolean',
'Function',
'RegExp',
'Element',
'NaN',
'Infinite'
].forEach(function (t) {
type['is' + t] = function (o) {
return type(o) === t.toLowerCase();
};
}); // examples:
type.isObject({}); // true
type.isNumber(NaN); // false
type.isElement(document.createElement('div')); // true
type.isRegExp(/abc/); // true

  这也是我们在移动网络应用中类型检测的JavaScript方法,而且我们发现这些代码很简单而且不容易出错。在gist里可以看到type方法的介绍。

编程笔记:JavaScript 中的类型检查的更多相关文章

  1. 小结 javascript中的类型检测

    先吐槽一下博客园的编辑器,太不好用了,一旦粘贴个表格进来就会卡死,每次都要用html编辑器写,不爽! 关于javascript的类型检测,早在实习的时候就应该总结,一直拖到现在,当时因为这个问题还出了 ...

  2. RX编程笔记——JavaScript 获取地理位置

    RX编程笔记——JavaScript 获取地理位置 2016-07-05

  3. Javascript学习1 - Javascript中的类型对象

    原文:Javascript学习1 - Javascript中的类型对象 1.1关于Numbers对象. 常用的方法:number.toString() 不用具体介绍,把数字转换为字符串,相应的还有一个 ...

  4. JavaScript中值类型和引用类型的区别

    JavaScript的数据类型分为两类:原始类型和对象类型.其中,原始类型包括:数字.字符串和布尔值.此外,JavaScript中还有两个特殊的原始值:null和undefined,它们既不是数字也不 ...

  5. javascript 中的类型

    javascript 中的类型 js 是一门弱语言,各式各样的错误多种多样,特别是确定返回值有问题的时候,你会用什么来进行表示错误? 我一般有三个选择: null '' error {} 第一个选择 ...

  6. JavaScript中Float类型保留两位小数

    JavaScript中Float类型保留两位小数 核心方法: num:要操作的数字     size:要保留的位数 parseFloat(num).toFixed(size); 实现代码如下:var  ...

  7. 【你不知道的javaScript 中卷 笔记1】javaScript中的类型与值

    一.类型与值 1.0 javaScript 七种内置类型: 空值(null) 未定义(undefined) 布尔值( boolean) 数字(number) 字符串(string) 对象(object ...

  8. js学习笔记----JavaScript中DOM扩展的那些事

    什么都不说,先上总结的图~   Selectors API(选择符API) querySelector()方法 接收一个css选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null ...

  9. javaScript中其他类型的值转换为Boolean类型

    将javaScript中其他任意类型的值转换为对应Boolean类型的值. 一  将number类型的值转换为Boolean类型 数值为0: var myBoolean = new Boolean(0 ...

随机推荐

  1. App接口如何保证安全

    微信开发或者高德地图,百度地图什么的api要使用,使用之前都需要注册一个账号,然后系统会给你一个key,然后调用api的时候把key传给服务器. 平常公司内部开发项目时,直接用mvc为app客户端提供 ...

  2. QTcpServer实现多客户端连接

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QTcpServer实现多客户端连接     本文地址:https://www.techiel ...

  3. PHP执行原理

    简单解释:PHP执行原理 客户端向服务器发送一个请求,如果请求的是一个HTML页面,服务器直接将HTML页面发送到客户端给浏览器解析,如果请求的是PHP页面,则服务器会运行PHP页面然后生成标准的HT ...

  4. App流量测试--使用安卓自身提供的TCP收发长度统计功能

    在Linux系统有3个地方保存流量统计文件,对于Android系统同样也适用: (1)在/proc/net/dev下可以查看各个网络接口的收发流量  (等同adb shell cat /proc/pi ...

  5. 【bzoj4372】烁烁的游戏 动态点分治+线段树

    题目描述 给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. 输入 第一行两个正整数:n,m接下来的n-1 ...

  6. Day22-1-知识回顾

    1. 知识点概要 --Session --CSRF --Model操作 --Form验证(ModelForm) --中间件 --缓存 2.知识回顾 2.1客户端请求及服务器端返回,都包含请求头和bod ...

  7. QT样式表

    QT样式表 一.QT样式表简介 1.QT样式表简介 QSS的主要功能是使界面的表现与界面的元素分离,使得设计皮肤与界面控件分离的软件成为可能. QT样式表是允许用户定制widgets组件外观的强大机制 ...

  8. [CTSC2012]熟悉的文章 后缀自动机

    题面:洛谷 题解: 观察到L是可二分的,因此我们二分L,然后就只需要想办法判断这个L是否可行即可. 因为要尽量使L可行,因此我们需要求出对于给定L,这个串最多能匹配上多少字符. 如果我们可以对每个位置 ...

  9. 【BZOJ2653】Middle(主席树)

    [BZOJ2653]Middle(主席树) 题面 BZOJ 洛谷 Description 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整.给你 ...

  10. Hystrix : 解决请求会被拒绝和抛出异常并且fallback不会被调用的问题

    启动脚本增加参数:-Dserver.tomcat.max-http-header-size=102400 增大Hystrix,ribbon的各项参数; hystrix: threadpool: def ...