使用Function.bind时节点转换stream怪癖

进入这个转换stream的行为,我不能解释。 首先设置:

var pass = new require('stream').PassThrough var numbers = ['one:', 'two:', 'three:', 'four:', 'five:', 'six:', 'seven:', 'eight:', 'nine:', 'ten:'] pass.pipe(process.stdout) 

好的,现在当我使用forEach写入stream时,我得到以下预期的行为:

 numbers.forEach(function(val) { pass.write(val) }) # one:two:three:four:five:six:seven:eight:nine:ten: 

但! 如果我以这样稍微不同的方式写入stream,我会得到以下意外的行为:

 numbers.forEach(pass.write.bind(pass)) # one:tthfoufivesix:seven:eight:nine:ten: 

所以我的问题是,为什么不同的产出? 我在这里错过了什么吗?

我正在运行v0.10.30

简短的回答:

.forEach将多个parameter passing给callback函数,因此直接传递callback函数时,不仅仅是将val传递给callback函数,还会将数组索引和整个数组传递给.write

很长的回答:

你的等价比较是错误的:

 numbers.forEach(pass.write.bind(pass)); 

一样的

 numbers.forEach(function(val) { pass.write(val) }); 

这相当于这样的:

 numbers.forEach(function() { pass.write.apply(pass, arguments); }); 

或者更具体地说:

 numbers.forEach(function(val, index, array) { pass.write(val, index /* encoding */, array /* cb */); }); 

当你查看.write(chunk, encoding, cb)的签名时,如果它不是一个函数,那么callback将被忽略,所以你基本上在做.write(val, index) 。 疯狂的输出是由于这些值在写入之前被传入Buffer ,所以你实质上是这样调用的:

 numbers.forEach(function(val, index, array) { pass.write(new Buffer(val, index /* encoding */)); }); 

分解成这样的东西:

 numbers.forEach(function(val, index, array) { var b = new Buffer(...); b.write(val, index /* encoding */); pass.write(b); }); 

这就是魔术发生的地方。 Buffer.prototype.write的签名是

 .write(string, [offset], [length], [encoding]) 

由于您将编码作为数字而不是实际编码传递给编码,因此使用编码值作为长度而不是编码的调用,因此您正在根据数组索引切分string长度。