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方法中引用。
当遇到错误时,这些测试能帮助我们更早地捕获错误,使得它们更容易诊断。但,这也可能扰乱代码库并潜在地影响应用程序的性能。是否使用防御性编程是一个成本和收益的问题。

提示

  • 避免强制转换和重载的混用

  • 考虑防御性地监视非预期的输入

转载于:https://www.cnblogs.com/wengxuesong/p/5652178.html

[Effective JavaScript 笔记]第59条:避免过度的强制转换相关推荐

  1. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  2. [Effective JavaScript 笔记]第29条:避免使用非标准的栈检查属性

    许多js环境都提供检查调用栈的功能.调用栈是指当前正在执行的活动函数链.在某些旧的宿主环境中,每个arguments对象含有两个额外的属性:arguments.callee和arguments.cal ...

  3. [Effective JavaScript 笔记]第23条:永远不要修改arguments对象

    arguments对象并不是标准的Array类型的实例.arguments对象不能直接调用Array方法. arguments对象的救星call方法 使得arguments可以品尝到数组方法的美味,知 ...

  4. [Effective JavaScript 笔记]第61条:不要阻塞I/O事件队列

    js程序是构建在事件之上的.输入可能来自不同的外部源.在一些语言中,我们习惯地编写代码来等待某个特定的输入. var text=downloadSync('http://example.com/fil ...

  5. [Effective JavaScript 笔记]第6章:库和API设计--个人总结

    前言 又到了一章的总结,这章里的内容.是把我从一个代码的使用者,如何换位成一个代码的编写者.如何让别人用自己的代码更容易,不用去注意太多的无用细节,不用记住冗长的函数名.在使用API时怎样避免使用者会 ...

  6. C++工作笔记-对static_cast的进一步认识(强制转换改变内存,并且删除存在的问题)

    static_cast关键字(编译时类型检查) 代码如下: #include <iostream> using namespace std; class Base{ public :Bas ...

  7. 学习笔记 | c++中四种类型cast(强制)转换

    c++中四种cast转换 C++四种强制转换方式,应用场景,细节 1.const_cast 用于将const变量转为非const. 用来修饰类型的const或volatile属性.除了去掉const或 ...

  8. python中如何编写代码输入多个数据并把它们放在一个列表中去_这59条编写高质量Python代码的方法你知道吗?...

    这个周末断断续续的阅读完了<Effective Python之编写高质量Python代码的59个有效方法>,感觉还不错,具有很大的指导价值. 下面将以最简单的方式记录这59条建议,并在大部 ...

  9. JavaScript 笔记

    学习笔记 部分转载自网络 #目录 [TOC] JavaScript 笔记 JavaScript负责页面中的的行为. 它是一门运行在浏览器端的脚本语言. JS的编写的位置 1.可以编写到标签的指定属性中 ...

最新文章

  1. Linux下oracle 9i图文安装二
  2. Redis数据库教程——系统详解学习Redis全过程
  3. 【Vegas原创】X connection to localhost:11.0 broken (explicit kill or server shutdown)解决方法...
  4. git版本管理工具学习
  5. window 2008+apache2.4.4+php5.5+mysql-5.6.12+phpmyadmin4.0.4.1安装过程(参考他人文章基础上加上自己遇到的问题)...
  6. 计算并输出1000以内的所有“完数”之和
  7. [论文阅读] An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale
  8. Linux下配置安装NFS
  9. [转载] IoT设备WiFi配网及现状
  10. 总结了一份嵌入式相关开源项目、库、资料
  11. 初学unity(简单场景制作)
  12. JDK 16 + eclips 安装配置
  13. 关于nomogram核心函数的time.inc函数的设定
  14. JavaScript网页滚动距离
  15. 百度AI人脸识别怎么实现,图片识别,文字识别,活体检测
  16. trans【Total3DUnderstanding】论文翻译+解读学习 2020.10.14
  17. app毕业设计题目基于Uniapp+SSM实现的android在线餐饮餐厅订餐点餐系统
  18. Java、JSP电影订票网站的设计与开发
  19. latex的图的排列方法_latex多幅图片排列 2*2示例
  20. 挂耳式耳机品牌排行榜,良心推荐这四款蓝牙耳机

热门文章

  1. EJB 3.0中会话Bean,实体Bean,消息驱动Bean的作用和例子
  2. golang避免XSS攻击
  3. 汇编实现地址对应值相加
  4. 8086地址传送指令LDS,LES
  5. mybaits十八:内置标签
  6. 史上最全!图解浏览器的工作原理
  7. 用js获取当前月份的天数
  8. 网络工程师必懂的专业术语
  9. 物联网与应用数字战略
  10. 树莓派发布全新计算模块CM3,性能提升10倍