[Effective JavaScript 笔记]第59条:避免过度的强制转换
js是弱类型语言。许多标准的操作符和代码库会把输入参数强制转换为期望的类型而不是抛出错误。如果未提供额外的逻辑,使用内置操作符的程序会继承这样的强制转换行为。
functin square(x){
return x*x;
}
square("3");//9
强制转换
强制转换可以带来方便性,但也会带来相关的麻烦,一些错误无法显露出来,导致程序行为的不稳定和难以调试。
当强制转换与重载的函数一起工作的时候,结果会更难理解。上一节讲的位向量类的enable方法。该方法使用其参数的类型来决定其行为。如果enable方法尝试将其参数强制转换为一个期望的类型,那么方法签名可能会变更难理解。将方法的参数强制转换为一个数字完全破坏了重载。
BitVector.prototype.enable=function(x){
x=Number(x);//转化为数字
if(typeof x=== 'number'){//一直是正确的
this.enableBit(x);
}else{//这里永远不会执行
for(var i=0,n=x.length;i < n;i++){
this.enableBit(x[i]);
}
}
};
一般规则,在那些使用参数类型来决定重载函数行为的函数中避免强制转换参数。强制转换使用很难识别出参数的变量。
bits.enable('100');//数字还是位数组值
调用者可以合理地认为参数可以是一个数字或一个位数组值,然而我们的构造函数并不是为字符串设计的,因此无法识别它。
防御性编程
可能是调用者没有用对,但如果设计API时,强制只接收数字和对象,则可以避免出现上面的错误。
BitVector.prototype.enable=function(x){
if(typeof x=== 'number'){
this.enableBit(x);
}else if(typeof x==='object' && object){
for(var i=0,n=x.length;i < n;i++){
this.enableBit(x[i]);
}
}else{
throw new TypeError('请输入一个数或类数组对象');
}
};
enable方法的最终版本是一种风格更加谨慎的示例,被称为防御性编程。防御性编程试图以额外的检查来抵御潜在的错误。抵御所有可能的错误是不可能的。如,我们可能使用检查来确保如果x具有length属性,那么它应该是一个对象,然而这并不是安全的,比如,一个意外使用的String对象。
监视函数
js除了提供实现检查的基本工具外,比如typeof操作符,还可以编写简洁的工具函数来监视函数签名。如,可以使用一个预先检查来监视BitVector的构造函数。
function BitVector(x){
uint32.or(arrayLike).guard(x);
//...
}
借助于共享原型对象来实现guard方法以构建一个监视对象的工具库。
var guard={
guard:function(x){
if(!this.test(x)){
throw new TypeError('expected '+this);
}
}
};
每个监视对象实现自己的test方法和错误消息的字符串描述。
uint32监视对象
var uint32=Object.create(guard);
uint32.test=function(x){
return typeof x === 'number' && x === (x >>> 0);
};
uint32.toString=function(){
return 'uint32';
};
uint32的监视对象使用js位操作符的一个诀窍来实现32位无符号整数的转换。无符号右移位运算符在执行移位运算前会将其第一个参数转换为一个32位的无符号整数。移入零位对整数值没有影响。实际上uint32.test是把一个数字与该数字转换为32位无符号整数的结果做比较。
arrayLike监视对象
下面实现arrayLike的监视对象。
var arrayLike=Object.create(guard);
arrayLike.test=function(x){
return typeof x==='object' && x && uint32.test(x.length);
};
arrayLike.toString=function(){
return 'array-like object';
};
这里又进一步地采取了防御性编程来确保一个类数组对象应该具有一个无符号整数的length属性。
“链”方法
最后,实现一些原型方法的“链”方法,比如or方法。
guard.or=function(other){
var res=Object.create(guard);
var self=this;
res.test=function(x){
return self.test(x)||other.test(x);
};
var description=this+' or '+other;
res.toString=function(){
return description;
};
return res;
}
该方法合并接受者监视对象和另一个监视对象,产生一个新的监视对象。新监视对象的test和toString方法合并了这两个输入对象的方法。这里用局部的self来保存this的引用,以确保能在合成的监视对象的test方法中引用。
当遇到错误时,这些测试能帮助我们更早地捕获错误,使得它们更容易诊断。但,这也可能扰乱代码库并潜在地影响应用程序的性能。是否使用防御性编程是一个成本和收益的问题。
提示
避免强制转换和重载的混用
考虑防御性地监视非预期的输入
[Effective JavaScript 笔记]第59条:避免过度的强制转换的更多相关文章
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记]第54条:将undefined看做“没有值”
undefined值很特殊,每当js无法提供具体的值时,就会产生undefined. undefined值场景 未赋值的变量的初始值即为undefined. var x; x;//undefined ...
- [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑
构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...
- [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合
对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...
- [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染
之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...
- [Effective JavaScript 笔记]第67条:绝不要同步地调用异步的回调函数
设想有downloadAsync函数的一种变种,它持有一个缓存(实现为一个Dict)来避免多次下载同一个文件.在文件已经被缓存的情况下,立即调用回调函数是最优选择. var cache=new Dic ...
随机推荐
- storm如何分配任务和负载均衡?
背景 在上篇:storm的基础框架分析 基本探讨了storm的: worker.executor等组件的关系. 线程模型和消息系统. 任务分配流程. topology提交到执行的过程. 但,感觉对ni ...
- 分享两个你可能不知道的Java小秘密
引言 最近LZ的工作发生了重大变化,以后博文的更新速度可能会再度回温,希望猿友们可以继续关注. 近期LZ辞掉了项目经理的工作,不过并未离开公司,是转到了基础研发部做更基础的研发,为广大技术人员服务.这 ...
- jquery源码分析
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 前段时间上班无聊之时,研究了 ...
- 《TCP/IP详解卷1:协议》第4章 ARP:地址解析协议-读书笔记
章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...
- 第十四课:js操作节点的插入,复制,移除
节点插入 appendChild方法,insertBefore方法是常用的两个节点插入方法,具体实现,请看js高级程序设计,或者自行百度. 这里提一下面试时经常会问到的问题,插入多个节点时,你是怎么插 ...
- nginx 的启动脚本
下载路径为: wget -q http://www.dwhd.org/script/Nginx-init-CentOS 根据自己的实际环境修改相应的参数 把该脚本放到/etc/rc.d/init.d/ ...
- 读JS高级——第五章-引用类型 _记录
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Java基础-多线程
介绍 操作系统能同时运行几个程序,每个独立运行的程序又称之为进程. 对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程.线程提供了多任务处理的能力 用进程和线程的观点来研究软件是当今普遍采 ...
- jQuery 文本编辑器插件 HtmlBox 使用
0.htmlbox下载地址:http://download.csdn.net/detail/leixiaohua1020/6376479 1.引入头文件 <script src="li ...
- PR 不能手动修改素材尺寸的解决方法
选中素材,然后再特效控制台那边点击一下运动就可以在预览窗口直接用鼠标调整画面大小和位移了.