Skip to content
Advertisement

Javascript function and expression function

This tiny Javascript program returns the sha256 hash of its text.

const shasum = require('crypto').createHash('sha256');
const stream = require('fs').createReadStream(__filename);

stream.on('error', function() { console.log('Error.'); });
stream.on('data', function (chunk) { shasum.update(chunk); }); /* <--- data line */
stream.on('end', function() {
    const sha = shasum.digest('base64');
    console.log(`The sha is ${sha}`);
});

Executed with Nodejs Erbium, it works as expected.

However, after writing it I thought that the function expression was not needed and so I changed the data line with the following:

stream.on('data', shasum.update);

And it crashes with a horrific error message:

  if (state[kFinalized])
           ^

TypeError: Cannot read property 'Symbol(kFinalized)' of undefined
    at ReadStream.update (internal/crypto/hash.js:78:12)
    at ReadStream.emit (events.js:311:20)
    at addChunk (_stream_readable.js:294:12)
    at readableAddChunk (_stream_readable.js:275:11)
    at ReadStream.Readable.push (_stream_readable.js:209:10)
    at internal/fs/streams.js:210:12
    at FSReqCallback.wrapper [as oncomplete] (fs.js:487:5)

Javascript is very flexible with function calls, but according to the documentation the stream.on data call should pass only one parameter.

Why is the behavior different?

Advertisement

Answer

The issue is the context.

The stream will bind the data function to the stream itself

stream.on('data', function (chunk) {
  console.log(this) // it is the `stream`
  shasum.update(chunk)
})

In this case, the shasum.update is bind to the stream, so the update function will not work:

function update(data, encoding) {
  const state = this[kState];
  if (state[kFinalized])
    throw new ERR_CRYPTO_HASH_FINALIZED();

To let it works you must write this statement:

stream.on('data', shasum.update.bind(shasum))
Advertisement