私の理論では、この動作は、node.jsキャッシュがどのように機能するか、ノードが循環依存関係を処理する方法、およびファイルをノードに直接require
ロードするときにコーヒートランスパイラーがどのように機能するかを組み合わせたものです。.coffee
私は、次のように、例示的なロギングを使用して、プログラムの個別のcoffeescriptバージョンとjavascriptバージョンを作成しました。「maincs」と「mainjs」を使用して、2つの言語が混在していないことを確認しました。
mainjs.js
console.log("mainjs starting");
console.log("mainjs exporting name");
module.exports = {
name: 'John'
};
console.log("mainjs requiring configjs");
var configjs = require('./configjs');
console.log("mainjs calling configjs.hello()");
configjs.hello();
configjs.js
console.log("configjs starting");
console.log("configjs requiring mainjs");
var mainjs = require("./mainjs");
console.log("configjs exporting hello");
module.exports = {
hello: function() {
return console.log("hello " + mainjs.name);
}
};
maincs.coffee
console.log "maincs starting"
console.log "maincs exporting name"
module.exports =
name: 'John'
console.log "maincs requiring configcs"
configcs = require './configcs'
console.log "maincs calling configcs.hello()"
configcs.hello()
configcs.coffee
console.log "configcs starting"
console.log "configcs requiring maincs"
maincs = require "./maincs"
console.log "configcs exporting hello"
module.exports =
hello: ->
console.log "hello #{maincs.name}"
したがって、それらを実行すると、異なる出力が得られます(これまで見てきたように)。以下に興味深い点を強調しました。
node mainjs.js
mainjs starting
mainjs exporting name
mainjs requiring configjs
configjs starting
configjs requiring mainjs #<--- Note the top-level mainjs.js code does not re-execute
configjs exporting hello
mainjs calling configjs.hello()
hello John
coffee maincs.coffee
maincs starting
maincs exporting name
maincs requiring configcs
configcs starting
configcs requiring maincs
maincs starting # <-- Look, the top-level maincs.coffee code is re-executing
maincs exporting name
maincs requiring configcs
maincs calling configcs.hello()
TypeError: Object #<Object> has no method 'hello'
したがって、この動作は、node.jsがシステムモジュールキャッシュを必要とする方法と、transpile-on-the-flyのcoffeescriptインタープリターに関係していると思います。基本的に、原因がモジュールmaincs = require "maincs"
内のトップレベルコードの再実行を引き起こす場合、ノードがエクスポートオブジェクトの未完成のコピーを提供しようとしている循環依存の状況に遭遇します。maincs
maincs
configcs
この動作を(少なくとも部分的に)説明している循環依存に関するnode.jsのドキュメントをお読みください。
ここで興味深いのは、必要になる前にhello
関数がエクスポートされていることを確認すれば、これを部分的に回避できることです。ただし、mainは最初の実行時に未定義になるため、コード内からの参照は機能しません。maincs
main.name
hello
maincs2.coffee
console.log "maincs2 starting"
console.log "maincs2 exporting name"
module.exports =
name: 'John'
console.log "maincs2 requiring configcs2"
configcs2 = require './configcs2'
console.log "maincs2 calling configcs2.hello()"
configcs2.hello()
configcs2.coffee
console.log "configcs2 starting"
console.log "configcs2 exporting hello"
module.exports =
hello: ->
console.log "hello from configcs"
console.log "configcs2 requiring maincs2"
maincs2 = require "./maincs2"
coffee maincs2.coffee
maincs2 starting
maincs2 exporting name
maincs2 requiring configcs2
configcs2 starting
configcs2 exporting hello
configcs2 requiring maincs2
maincs2 starting
maincs2 exporting name
maincs2 requiring configcs2
maincs2 calling configcs2.hello()
hello from configcs
maincs2 calling configcs2.hello()
hello from configcs
したがって、基本的に、この動作は、モジュールがキャッシュされるrequire
方法、循環依存関係と相互作用する方法、およびコーヒートランスパイラー自体のいくつかの側面の両方の組み合わせです。.coffeeを.jsにトランスパイルし、ノードで実行すると、IIFEラッパーを使用する通常のコーヒースクリプトとラッパーを使用しないベアコーヒースクリプトの両方でこの問題が回避されるため、IIFEラッパーは重要ではないようです(ノードは基本的に独自のラッパーを追加します)。