[Effective JavaScript 笔记]第55条:接收关键字参数的选项对象
53节建议保持参数顺序的一致约定对于帮助程序员记住每个参数在函数调用中的意义很重要。参数较少这个主意不错,但如果参数过多后,就出现麻烦了,记忆和理解起来都不太容易。
参数蔓延
如下面这些代码:
var alert=new Alert(100,75,300,200,'Error',message,'blue','white','black','error',true);
这个通常是参数蔓延的结果。一个函数起初很简单,然而随着库功能的扩展,该函数的签名便会获得越来越多的参数。
选项对象
js提供了一个简单、轻量的惯用法:选项对象。选项对象在应对较大规模的函数签名时运作良好。一个选项参数就是一个通过其命名属性来提供额外参数数据的参数。对象字面量的形式使得读写选项对象尤其舒适。
var alert=new Alert({
x:100,y:75,
width:300,height:200,
title:'Error',message:message,
titleColor:'blue',bgColor:'white',textColor:'black',
icon:'error',modal:true
});
自我说明性
这样实现选项对象显得繁琐,但更容易阅读。每个参数都是自我描述的。不需要注释来解释各参数的职责,因为其属性名就是一种说明。对于布尔参数,如果只是传递true或false并不能提供让人明白它代表的意思,但用属性名modal可以更清晰地说明它的用途。
参数可选性
选项对象的所有参数都是可选的,调用者可以提供任一可选参数的子集。与普通参数(有时也叫位置参数,因为它们的位置是和形参一一对应的)相比,可选参数通常会引入一些歧义。例如,如果希望Alert对象的位置和大小属性都是可选的,那么很难理解如下的调用。
var alert=new Alert(app,150,150,'Error',message,'blue','white','black','error',true);
这里在使用可选参数时就造成了麻烦,因为不知道省略的是哪些参数,因为x,y和width,height无法区分。使用选项对象就没有任何问题(不依赖于参数的顺序,只依赖与参数的属性名)。
var alert=new Alert({
width:300,height:200,
title:'Error',message:message,
titleColor:'blue',bgColor:'white',textColor:'black',
icon:'error',modal:true
});
选项对象仅包括可选参数,因此省略掉整个对象甚至都是可能的。
var alert=new Alert();
如果只有一个或两个必选的参数,最好使它们独立于选项对象。
var alert=new Alert(app,message,{
width:300,height:200,
title:'Error',
titleColor:'blue',bgColor:'white',textColor:'black',
icon:'error',modal:true
});
实现一个接收选项对象的函数需要额外的代码处理。代码如下:
function Alert(parent,message,opts){
opts=opts||{};
this.width=opts.width===undefined?320:opts.width;
this.height=opts.height===undefined?240:opts.height;
this.x=opts.x===undefined?(parent.width)/2-(this.width/2):opts.x;
this.y=opts.y===undefined?(parent.height)/2-(this.height/2):opts.y;
this.title=opts.title||'Alert';
this.titleColor=opts.titleColor||'gray';
this.bgColor=opts.bgColor||'white';
this.textColor=opts.textColor||'black';
this.icon=opts.icon||'info';
this.modal=!!opts.modal;
this.message=message;
}
这里对opts使用了或(||)操作符提供了一个默认空选项对象。由于0是一个有效值但不是默认值,所以需要测试数值参数是否为undefined。基于空字符串是无效的、应该被默认值取代的假设,这里使用逻辑或来应对字符串参数。modal参数使用双重否定模式将其参数强制转换为一个布尔值。
extend函数
与位置参数对比,这段代码比较烦琐。可以使用有用的抽象来简化这些工作。(对象的扩展或合并函数)。比如许多库提供的extend函数。该函数接收一个target对象和一个source对象,并将后者的属性复制到前者中。该程序最有用的应用之一是抽象出合资默认值和用户提供的选项对象值的逻辑。借助extend函数,代码改写为
function Alert(parent,message,opts){
opts=extend({width:320,height:240});
opts=extend({
x:(parent.width)/2-(opts.width/2):opts.x,
y:(parent.height)/2-(opts.height/2):opts.y,
title:'Alert',
titleColor:'gray',
bgColor:'white',
textColor:'black',
icon:'info',
modal:false
},opts);
this.width=opts.width;
this.height=opts.height;
this.x=opts.x;
this.y=opts.y;
this.title=opts.title;
this.titleColor=opts.titleColor;
this.bgColor=opts.bgColor;
this.textColor=opts.textColor;
this.icon=opts.icon;
this.modal=opts.modal;
this.message=message;
}
这避免了不断地重复实现检查每个参数是否存在的逻辑。这里两次调用了extend函数,因为,x,y的默认值依赖于早前计算的width,height的值。
如果想要把整个opts复制到this对象,可以进一步简化。
function Alert(parent,message,opts){
opts=extend({width:320,height:240});
opts=extend({
x:(parent.width)/2-(opts.width/2):opts.x,
y:(parent.height)/2-(opts.height/2):opts.y,
title:'Alert',
titleColor:'gray',
bgColor:'white',
textColor:'black',
icon:'info',
modal:false
},opts);
extend(this,opts);
}
不同的框架提供的extend函数不同,典型的实现是枚举源对象的属性,并当这些属性不是undefined时将其复制到目标对象中。
function extend(target,source){
if(source){
for(var key in source){
var val=source[key];
if(typeof val !== 'undefined'){
target[key]=val;
}
}
}
return target;
}
区别
原来的Alert版本和使用extend函数实现的版本的区别
早期版本中的条件逻辑如果不需要默认值则会避免计算默认值。只要计算默认值对诸如修改用用户接口或发送网络请求没有影响,那么这不是一个问题。
判断一个值是否已经提供了的逻辑。在早前版本中,对于字符串参数,我们将空字符串视为undefined等价。只将undefined视为缺省的参数更恰当。使用或(||)操作符是一个提供默认参数值有效但非一致的策略。
一致性是库设计的一个良好目标,因为它会给api的使用者带来更好的可预测性。
提示
使用选项对象使得api更具可读性、更容易记忆
所有通过选项对象提供的参数应当被视为可选的
使用extend函数抽象出从选项对象中提取值的逻辑
相关阅读
[Effective JavaScript 笔记]第55条:接收关键字参数的选项对象的更多相关文章
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记]第23条:永远不要修改arguments对象
arguments对象并不是标准的Array类型的实例.arguments对象不能直接调用Array方法. arguments对象的救星call方法 使得arguments可以品尝到数组方法的美味,知 ...
- [Effective JavaScript 笔记] 第8条:尽量少用全局对象
初学者容易使用全局变量的原因 创建全局变量毫不费力,不需要任何形式的声明(只要在非函数里用var 你就可以得到一个全局变量) 写得代码简单,不涉及到大的项目或配合(写hello world是不会有什么 ...
- [Effective JavaScript 笔记]第24条:使用变量保存arguments对象
迭代器(iterator)是一个可以顺序存取数据集合的对象.其一个典型的API是next方法.该方法获得序列中的下一个值. 迭代器示例 题目:希望编写一个便利的函数,它可以接收任意数量的参数,并为这些 ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合
对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...
- [Effective JavaScript 笔记]第53条:保持一致的约定
对于api使用者来说,你所使用的命名和函数签名是最能产生普遍影响的决策.这些约定很重要具有巨大的影响力.它建立了基本的词汇和使用它们的应用程序的惯用法.库的使用者必须学会阅读和使用这些.一致的约定可以 ...
随机推荐
- 『片段』OracleHelper (支持 多条SQL语句)
C# 调用 Oracle 是如此尴尬 >System.Data.OracleClient.dll —— .Net 自带的 已经 过时作废. >要链接 Oracle 服务器,必须在 本机安装 ...
- Gensim进阶教程:训练word2vec与doc2vec模型
本篇博客是Gensim的进阶教程,主要介绍用于词向量建模的word2vec模型和用于长文本向量建模的doc2vec模型在Gensim中的实现. Word2vec Word2vec并不是一个模型--它其 ...
- jquery的基本动画方法
1 在使用$.extent()的时候,我们一般不放function类型,如果放的话,提前测试下. ?2 Function类型是一种基本类型还是引用类型呢. 3 $('<div>') 指创建 ...
- 每天一个linux命令(35):killall命令
Linux 系统中的killall命令用于杀死指定名字的进程(kill processes by name).我们可以使用kill命令杀死指定进程PID的进 程,如果要找到我们需要杀死的进程,我们还需 ...
- Webform之Repeater中的单选和多选的应用以及前段JS的实现
HTML中的代码 <asp:Repeater ID="Repeater1" runat="server"> <HeaderTemplate&g ...
- XCode7继续用http协议解决办法
昨天被苹果放鸽子也没升级iOS9,今天升级了Xcode7,同时手机升级了iOS9,发现项目报错,查了查才知道是iOS9不支持不安全的http传输协议,让用https协议,这根本就不x现实,,服务端根本 ...
- 修改Oracle权限的SQL及常见错误
1.在cmd命令中进入sqlplus:相应的在DOS命令下执行:(1)set ORACLE_SID = $INSTANCE_NAME(2)sqlplus /nolog(3)connect user/p ...
- opencv笔记3:trackbar简单使用
time:2015年 10月 03日 星期六 13:54:17 CST # opencv笔记3:trackbar简单使用 当需要测试某变量的一系列取值取值会产生什么结果时,适合用trackbar.看起 ...
- BZOJ-1934 Vote 善意的投票 最大流+建图
1934: [Shoi2007]Vote 善意的投票 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 1551 Solved: 951 [Submit][S ...
- Server Data Synchronization Via Linux rsync、rsync+inotify Between Load Balance Server
目录 . 远程文件同步的应用场景 . rsync+crontab . rsync+inotify 1. 远程文件同步的应用场景 在负载均衡集群的应用场景中,往往在多台web server的前端有一个提 ...