第六章 库和API设计
53、保持一致的约定
在变量命名和函数签名中使用一致的约定
不要偏离用户在他们的开发平台中很可能遇到的约定
54、将undefined看作没有值
将undefined看作缺少某个特定的值是Javascript语音建立的一种公约
避免使用undefined表示任何非特定值
在允许0、NaN或空字符串为有效参数的地方,绝不要通过真值测试来实现默认值
55、接收关键字参数的选项对象
JS提供了一个简单、轻量的惯用法:选项对象(options object)
框架都提供一个extend函数合并默认值与选项对象
56、避免不必要的状态
API归为两类:有状态和无状态
无状态:函数或方法的行为只取决于输入,而与程序的状态无关。字符串的状态是无状态的,字符串的内部不能被修改“foo”.toUpperCase()总是产生FOO
Date对象是有状态的,各种set方法将Date的属性修改,toString会产生不同的结果
c.fillStyle="blue";c.font="24pt self"; 有状态
c.fillText({fillStyle:"blue",font:"24pt self"}) 无状态
57、使用结构类型设计灵活的接口
这种接口有时候成为结构类型(structural typing)或鸭子类型(duck typing)
针对单元测试,使用mock对象即接口的替代实现来提供可复验的行为
58、区分数组对象和类数组对象
猜测一个对象是否实现了结构类型有时被称为鸭子测试(duck testing)
JS的数组就是对象,真正想做的是分离数组对象和非数组对象
API绝不应该重载与其他类型有重叠的类型
Array.isArray比instanceof可靠 toString.call(x) === '[object Array]'
59、避免过度的强制转换
防御性编程试图以额外的检查来抵御潜在的错误
避免强制转换与重载的混用
60、支持方法链
使用方法链连接无状态的操作
无状态的方法中返回新对象支持方法链
有状态的方法中返回this支持方法链
在变量命名和函数签名中使用一致的约定
不要偏离用户在他们的开发平台中很可能遇到的约定
54、将undefined看作没有值
将undefined看作缺少某个特定的值是Javascript语音建立的一种公约
避免使用undefined表示任何非特定值
在允许0、NaN或空字符串为有效参数的地方,绝不要通过真值测试来实现默认值
55、接收关键字参数的选项对象
JS提供了一个简单、轻量的惯用法:选项对象(options object)
框架都提供一个extend函数合并默认值与选项对象
56、避免不必要的状态
API归为两类:有状态和无状态
无状态:函数或方法的行为只取决于输入,而与程序的状态无关。字符串的状态是无状态的,字符串的内部不能被修改“foo”.toUpperCase()总是产生FOO
Date对象是有状态的,各种set方法将Date的属性修改,toString会产生不同的结果
c.fillStyle="blue";c.font="24pt self"; 有状态
c.fillText({fillStyle:"blue",font:"24pt self"}) 无状态
57、使用结构类型设计灵活的接口
Wiki.formats.MEDIAWIKI = function() { return { getTitle: function(){}, getAuthor: function(){}, toHtml: function(){} }; }
针对单元测试,使用mock对象即接口的替代实现来提供可复验的行为
58、区分数组对象和类数组对象
猜测一个对象是否实现了结构类型有时被称为鸭子测试(duck testing)
JS的数组就是对象,真正想做的是分离数组对象和非数组对象
API绝不应该重载与其他类型有重叠的类型
Array.isArray比instanceof可靠 toString.call(x) === '[object Array]'
59、避免过度的强制转换
防御性编程试图以额外的检查来抵御潜在的错误
避免强制转换与重载的混用
60、支持方法链
使用方法链连接无状态的操作
无状态的方法中返回新对象支持方法链
有状态的方法中返回this支持方法链
第七章 并发
61、不要阻塞I/O事件队列
异步API使用回调函数来延缓处理代价高昂的操作以避免阻塞主应用程序
JS并发的接收事件,但会使用一个事件队列按序地处理事件处理程序
62、在异步的序列中使用嵌套或命名的回调函数
使用嵌套或命名的回调函数按序地执行多个异步操作
尝试在过多的嵌套回调函数和尴尬的命名的非嵌套回调函数之间取得平衡
避免将可被并行执行的操作序列化
63、当心丢弃错误
异步的API倾向于将错误表示为回调函数的特定参数,或使用一个附加的错误处理回调函数
另一种风格,只需要一个回调函数,该回调函数的第一个参数如果发生错误就表示为一个错误
通过编写共享的错误处理函数避免复制和黏贴错误处理代码
确保明确的处理所有的错误条件以避免丢弃错误
64、对异步循环使用递归
递归函数试图调用自身10万次,大多数会产生一个运行时错误,栈溢出会耗尽栈空间
分析:JS环境通常在内存中保存一块固定的区域,称为调用栈,用于记录函数调用返回前下一步做什么
异步API在其回调函数被调用前会立即返回
65、不要在计算时阻塞事件队列
避免在主事件队列中执行代价高昂的算法
将计算程序分解到事件循环的多个轮次中
66、使用计数器来执行并行操作
程序的执行顺序不能保证与事件发生的顺序一致
当一个应用程序依赖特定的时间顺序才能正常工作的时,这个程序会遭受到数据竞争(data race) 数据竞争是指多个并发操作可以修改共享的数据结构,这取决于他们发生的顺序
67、绝不要同步地调用异步的回调函数
同步的调用异步的回调函数扰乱了预期的操作序列,并可能导致意想不到的交错代码
同步的调用异步的回调函数可能导致栈溢出或错误的处理异常
使用异步API,比如setTime函数来调度异步回调函数,使其运行于另一个回合
异步API使用回调函数来延缓处理代价高昂的操作以避免阻塞主应用程序
JS并发的接收事件,但会使用一个事件队列按序地处理事件处理程序
62、在异步的序列中使用嵌套或命名的回调函数
使用嵌套或命名的回调函数按序地执行多个异步操作
尝试在过多的嵌套回调函数和尴尬的命名的非嵌套回调函数之间取得平衡
避免将可被并行执行的操作序列化
63、当心丢弃错误
异步的API倾向于将错误表示为回调函数的特定参数,或使用一个附加的错误处理回调函数
另一种风格,只需要一个回调函数,该回调函数的第一个参数如果发生错误就表示为一个错误
downloadAsyn("a.txt", function(error, a) {
if(error) onError(error);
....
});
确保明确的处理所有的错误条件以避免丢弃错误
64、对异步循环使用递归
递归函数试图调用自身10万次,大多数会产生一个运行时错误,栈溢出会耗尽栈空间
function countdown(n) { if(n === 0) return "down"; else { return countdown(n-1); } }
|
(script start)
|
console.log();
|
|
countdown(100000)
|
return countdown(.);
|
|
countdown(99999)
|
return countdown(.);
|
| ... | ... |
|
countdown(0)
|
return countdown(.);
|
65、不要在计算时阻塞事件队列
避免在主事件队列中执行代价高昂的算法
将计算程序分解到事件循环的多个轮次中
66、使用计数器来执行并行操作
程序的执行顺序不能保证与事件发生的顺序一致
当一个应用程序依赖特定的时间顺序才能正常工作的时,这个程序会遭受到数据竞争(data race) 数据竞争是指多个并发操作可以修改共享的数据结构,这取决于他们发生的顺序
67、绝不要同步地调用异步的回调函数
var cache = new Dict(); function downLoadCachingAsync(url, onsuccess, onerror) { if(cache.has(url)) { var cached = cache.get(url); setTimeout(onsuccess.bind(null, cache), 0); } return downloadAsync(url, function(file) cache.set(url, file); onsuccess(file); }, onerror) }
同步的调用异步的回调函数可能导致栈溢出或错误的处理异常
使用异步API,比如setTime函数来调度异步回调函数,使其运行于另一个回合
posted on
浙公网安备 33010602011771号