除使用众所周知的 call 和 apply 外,总是优先选择 .bind( this ) 或者一个功能上等价于它的。创建 BoundFunction 声明供后续调用,当没有更好的选择时才使用别名。
复制代码 代码如下:
// 6.B.1
function Device( opts ) {
this.value = null;
// 新建一个异步的 stream,这个将被持续调用
stream.read( opts.path, function( data ) {
// 使用 stream 返回 data 最新的值,更新实例的值
this.value = data;
}.bind(this) );
// 控制事件触发的频率
setInterval(function() {
// 发出一个被控制的事件
this.emit("event");
}.bind(this), opts.freq || 100 );
}
// 假设我们已继承了事件发送器(EventEmitter) ;)
当不能运行时,等价于 .bind 的功能在多数现代 JavaScript 库中都有提供。
复制代码 代码如下:
// 6.B.2
// 示例:lodash/underscore,_.bind()
function Device( opts ) {
this.value = null;
stream.read( opts.path, _.bind(function( data ) {
this.value = data;
}, this) );
setInterval(_.bind(function() {
this.emit("event");
}, this), opts.freq || 100 );
}
// 示例:jQuery.proxy
function Device( opts ) {
this.value = null;
stream.read( opts.path, jQuery.proxy(function( data ) {
this.value = data;
}, this) );
setInterval( jQuery.proxy(function() {
this.emit("event");
}, this), opts.freq || 100 );
}
// 示例:dojo.hitch
function Device( opts ) {
this.value = null;
stream.read( opts.path, dojo.hitch( this, function( data ) {
this.value = data;
}) );
setInterval( dojo.hitch( this, function() {
this.emit("event");
}), opts.freq || 100 );
}
提供一个候选,创建一个 this 的别名,以 self 作为标识符。这很有可能出 bug,应尽可能避免。
复制代码 代码如下:
// 6.B.3
function Device( opts ) {
var self = this;
this.value = null;
stream.read( opts.path, function( data ) {
self.value = data;
});
setInterval(function() {
self.emit("event");
}, opts.freq || 100 );
}
C. 使用 thisArg
好几个 ES 5.1 中的原型的方法都内置了一个特殊的 thisArg 标记,尽可能多地使用它
复制代码 代码如下:
// 6.C.1
var obj;
obj = { f: "foo", b: "bar", q: "qux" };
Object.keys( obj ).forEach(function( key ) {
// |this| 现在是 `obj`
console.log( this[ key ] );
}, obj ); // <-- 最后的参数是 `thisArg`
// 打印出来...
// "foo"
// "bar"
// "qux"
thisArg 在 Array.prototype.every、 Array.prototype.forEach、 Array.prototype.some、 Array.prototype.map、 Array.prototype.filter 中都可以使用。
七、Misc
这个部分将要说明的想法和理念都并非教条。相反更鼓励对现存实践保持好奇,以尝试提供完成一般 JavaScript 编程任务的更好方案。
A. 避免使用 switch,现代方法跟踪(method tracing)将会把带有 switch 表达式的函数列为黑名单。
似乎在最新版本的 Firefox 和 Chrome 都对 switch 语句有重大改进。
值得注意的是,改进可以这里看到: https://github.com/rwldrn/idiomatic.js/issues/13
复制代码 代码如下:
// 7.A.1.1
// switch 语句示例
switch( foo ) {
case "alpha":
alpha();
break;
case "beta":
beta();
break;
default:
// 默认分支
break;
}
// 7.A.1.2
// 一个可支持组合、重用的方法是使用一个对象来存储 “cases”,
// 使用一个 function 来做委派:
var cases, delegator;
// 返回值仅作说明用
cases = {
alpha: function() {
// 语句
// 一个返回值
return [ "Alpha", arguments.length ];
},
beta: function() {
// 语句
// 一个返回值
return [ "Beta", arguments.length ];
},
_default: function() {
// 语句
// 一个返回值
return [ "Default", arguments.length ];
}
};
delegator = function() {
var args, key, delegate;
// 把 `argument` 转换成数组
args = [].slice.call( arguments );
// 从 `argument` 中抽出最前一个值
key = args.shift();
// 调用默认分支
delegate = cases._default;
// 从对象中对方法进行委派操作
if ( cases.hasOwnProperty( key ) ) {
delegate = cases[ key ];
}
// arg 的作用域可以设置成特定值,
// 这种情况下,|null| 就可以了
return delegate.apply( null, args );
};
// 7.A.1.3
// 使用 7.A.1.2 中的 API:
delegator( "alpha", 1, 2, 3, 4, 5 );
// [ "Alpha", 5 ]
// 当然 `case` key 的值可以轻松地换成任意值
var caseKey, someUserInput;
// 有没有可能是某种形式的输入?
someUserInput = 9;
if ( someUserInput > 10 ) {
caseKey = "alpha";
} else {
caseKey = "beta";
}
// 或者...
caseKey = someUserInput > 10 ? "alpha" : "beta";
// 然后...
delegator( caseKey, someUserInput );
// [ "Beta", 1 ]
// 当然还可以这样搞...
delegator();
// [ "Default", 0 ]
B. 提前返回值提升代码的可读性并且没有太多性能上的差别
复制代码 代码如下:
// 7.B.1.1
// 不好:
function returnLate( foo ) {
var ret;
if ( foo ) {
ret = "foo";
} else {
ret = "quux";
}
return ret;
}
// 好:
function returnEarly( foo ) {
if ( foo ) {
return "foo";
}
return "quux";
}
八、原生 & 宿主对象(注:其实一直觉得 Host Objects 真不应该翻译过来,这是就按一般书的写法翻出来吧)
最基本的原则是:
不要干任何蠢事,事情总会变好的。
为了加强这个观念,请观看这个演示:
“一切都被允许: 原生扩展” by Andrew Dupont (JSConf2011, Portland, Oregon)
九、注释
单行注释放于代码上方为首选
多行也可以
行末注释应被避免!
JSDoc 的方式也不错,但需要比较多的时间
十、单用一门语言
无论是什么语言程序维护者(或团队)规定使用何种语言,程序都应只用同一种语言书写。
附录
前置逗号(Comma First)
所有使用这个文档作为基本风格指南的项目都不允许前置逗号的代码格式,除非明确指定或者作者要求。