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条:避免过度的强制转换的更多相关文章

  1. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  2. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

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

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

  4. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  5. [Effective JavaScript 笔记]第54条:将undefined看做“没有值”

    undefined值很特殊,每当js无法提供具体的值时,就会产生undefined. undefined值场景 未赋值的变量的初始值即为undefined. var x; x;//undefined ...

  6. [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

    构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...

  7. [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合

    对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...

  8. [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染

    之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...

  9. [Effective JavaScript 笔记]第67条:绝不要同步地调用异步的回调函数

    设想有downloadAsync函数的一种变种,它持有一个缓存(实现为一个Dict)来避免多次下载同一个文件.在文件已经被缓存的情况下,立即调用回调函数是最优选择. var cache=new Dic ...

随机推荐

  1. 扫描函数sweep

    groot.sweep(element,vm); element 为要扫描的代码片段,vm 对应的一个 groot.view 定义的 一个Vm的整体 扫面代码 groot.sweep = functi ...

  2. 大型网站系统架构实践(五)深入探讨web应用高可用方案

    从上篇文章到这篇文章,中间用了一段时间准备,主要是想把东西讲透,同时希望大家给与一些批评和建议,这样我才能有所进步,也希望喜欢我文章的朋友,给个赞,这样我才能更有激情,呵呵. 由于本篇要写的内容有点多 ...

  3. [AaronYang]C#人爱学不学[2]

    1. 记事本写C#,脱离vs 新建记事本,名字为 helloworld.cs using System; namespace Hello{ public class HelloWorldSay{ st ...

  4. http状态码介绍

    基本涵盖了所有问题HTTP 400 – 请求无效HTTP 401.1 – 未授权:登录失败HTTP 401.2 – 未授权:服务器配置问题导致登录失败HTTP 401.3 – ACL 禁止访问资源HT ...

  5. cursor:pointer的意思

    cursor规则是设定网页浏览时用户鼠标指针的样式,也就是鼠标的图形形状cursor:pointer设定鼠标的形状为一只伸出食指的手,这也是绝大多数浏览器里面鼠标停留在网页链接上方时候的样式另外可以选 ...

  6. codevs2495 水叮当的舞步 IDA*

    我打暴力不对,于是就看看题解,,,,,,IDA*就是限制搜索深度而已,这句话给那些会A*但不知道IDA*是什么玩意的小朋友 看题解请点击这里 上方题解没看懂的看看这:把左上角的一团相同颜色的范围,那个 ...

  7. Informatica 错误:Cannot convert from SQL type 93 to C type 4

    经验和积累蛮重要!向大神学习! ---------------------------------------------------------------------- Mapping: 在sou ...

  8. UVa 437 The Tower of Babylon

    Description   Perhaps you have heard of the legend of the Tower of Babylon. Nowadays many details of ...

  9. hihocoder 1154 Spring Outing

    传送门 #1154 : Spring Outing 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 You class are planning for a spring ...

  10. 循序渐进Linux 3:Linux下软件安装与管理

    一.源码安装 ./configuremakemake install 二.RPM包 1. 安装软件包 rpm -i [辅助选项] file1.rpm file2.rpm主选项 -i: install, ...