Node.js
ECMAScript
WHATWG
UniversalJS

Universal JSで注意すべきJS/Node.jsの違い

Universal JS

  • JavaScript/Node.jsは、同じ言語なのでClient/Server Sideでコードを共有可能
    • Validationなど共有可能
  • けれど各moduleのAPIの仕様は必ずしも一致しない
    • 仕様がどこにあるか、を知ることが重要

Q. JavaScriptのTimer APIの仕様はどこにあるでしょう?


A. whatwg

  • "setTimeout"と検索
  • MDN/MSDNなどが上位にくるので開く
    • 片方しかない場合は、vendorがそのAPIを独自実装している可能性
  • vendorがspec決めてる?
    • ex, MDNなら「仕様」とい う欄があるのでそこのリンクを辿ると......
  • whatwgが決めていた
    • w3c/whatwgはDOM(html等も含む)の仕様を定義
      • ただしvendorが従っている保証はない
    • EcmaはJSのruntimeのみ定義

Q. JSとNode.jsのTimer APIとでは仕様に違いがありますか?


A. JSとNode.jsのTimer APIの違い

  • returnの型
    • JSはID(number)
    • Node.jsはTimeout obj
      • timeout.ref(), timeout.unref()という便利な関数がある
  • setImmediate API
    • 各種ベンダーの内、MSのみdraftを定義
    • Node.jsも同時期に開発したがspecが全然違う
  • setTimeout(() => console.log('ok'), 0)

Q. JavaScriptのConsole APIの仕様はどこにあるでしょう?


A. draft段階(標準化されてない)


Q. Node.jsとEcmaScriptとで、Errorの仕様に違いはありますか?


A. あります

Errorオブジェクトのプロパティの観点で違いをみてみる

  • ECMAScript 2015: Error の定義
    • Error.prototype
    • Error.prototype.constructor
    • Error.prototype.message
    • Error.prototype.name
    • Error.prototype.toString()
  • Node独自の追加
    • error.code
    • error.errno
    • error.syscall
    • stack系はdocに書かれている通りv8独自の追加
  • ベンダー独自の拡張(非標準)
    • Error.prototype.description (MS)
    • Error.prototype.number (MS)
    • Error.prototype.fileName (Mozilla)
    • Error.prototype.lineNumber (Mozilla)
    • Error.prototype.columnNumber (Mozilla)
    • Error.prototype.stack (Mozilla/v8)
    • Error.captureStackTrace (v8)
    • Error.stackTraceLimit (v8)

Node.jsのmodule固有のError property

以前は記載がなかったので、Node.jsのdocにPRしました
https://github.com/nodejs/node/pull/10986

module property
net/http(s) error.address
net/http(s) error.port
fs error.path
// with `path` property
$node -e "require('fs').readFile('a file that does not exist', (err, data) => { console.error(err); });"
{ Error: ENOENT: no such file or directory, open 'a file that does not exist'
    at Error (native)
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: 'a file that does not exist' }

// with `address` and `port` property
$node -e "require('net').connect({port: 100}).on('error', (err) => { console.error(err); });"
{ Error: connect ECONNREFUSED 127.0.0.1:100
    at Object.exports._errnoException (util.js:1022:11)
    at exports._exceptionWithHostPort (util.js:1045:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1087:14)
  code: 'ECONNREFUSED',
  errno: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 100 }

// with `address` property (without `port` property)
$node -e "require('net').connect({host: 'localhost'}).on('error', (err) => { console.error(err); });"
{ Error: connect EADDRNOTAVAIL 127.0.0.1 - Local (0.0.0.0:54881)
    at Object.exports._errnoException (util.js:1022:11)
    at exports._exceptionWithHostPort (util.js:1045:20)
    at connect (net.js:881:16)
    at net.js:1010:7
    at GetAddrInfoReqWrap.asyncCallback [as callback] (dns.js:62:16)
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:81:10)
  code: 'EADDRNOTAVAIL',
  errno: 'EADDRNOTAVAIL',
  syscall: 'connect',
  address: '127.0.0.1' }