2012-01-09

「JavaScriptの配列は参照渡し(call-by-reference)」は間違い

JavaScriptの配列は『参照渡し(call-by-reference)』」というネット上に大量に存在する間違った記述を訂正するエントリ。

結論から先に言うと

JavaScriptにおいて、関数の引数として配列を与えた場合、『参照の値渡し』になります。『参照の値渡し』は、『参照渡し(call-by-reference)』ではなく『値渡し(call-by-value)』に分類されます。

参考エントリ

以下の解説が非常にわかりやすいです。

G-chan Square - [javascript] javascriptの関数で引数に配列を渡すと、それは本当に参照渡しか?
G-chan Square - じゃ、「参照渡し」ってなんだ?

簡単に端折ると、関数の引数として変数を与える場合、

  1. 値の値渡し(プリミティブ型変数の値をそのまま渡す)
  2. 値の参照渡し(プリミティブ型変数の参照を渡す)
  3. 参照の値渡し(参照型変数のもつ値を値として渡す)
  4. 参照の参照渡し(参照型変数自体の参照を渡す)

の4パターンを考えることができて、プログラム言語用語における『参照渡し(call-by-reference)』はパターン2とパターン4を指すのだけれど、パターン3を『参照渡し(call-by-reference)』と呼んでしまっている人が存在する、というお話。JavaScriptの配列に関しては、このパターン3に該当します。

誤解の原因

『参照渡し(call-by-reference)』という用語の誤解

参照渡し/値渡しという区別は、
foo(a);
foo(a[i]);
において、「a」「a[i]」が左辺値として扱われるか右辺値として扱われるか
のことです。

たとえ、変数「a」や配列の要素「a[i]」が保持する値が「オブジェクトへの
参照」であろうが、
foo(a);
foo(a[i]);
のことを、「参照渡し(call-by-reference)」とは呼びません。

G-chan Square - じゃ、「参照渡し」ってなんだ?

というわけで、「JavaScriptにおいて配列は参照型である」ということを根拠に「配列は『参照渡し(call-by-reference)』である」とするのは間違いです。

不十分なテスト

ネット上でよく見かけたのは、「引数として与えた配列の要素を関数内で変更することができたから、これは『参照渡し(call-by-reference)』である」という主張です。

function f(x) {
  x[0] = 1;
}

var a = new Array(0, 0, 0);
f(a);
console.log(a);

を実行したら

[1, 0, 0]

と出力されたから、ちゃんと配列の要素が変更されているじゃないか、『参照渡し(call-by-reference)』だ、というわけです。

しかし、このテストによってわかるのは、「配列データ(例では[0, 0, 0])のコピーが関数に与えられているわけではない」ということ、つまりパターン1ではないということだけです。「JavaScriptにおいて配列は参照型である」ということがわかっているなら、既にパターン1でないことは明らかなので、テストとして意味がありません。依然としてパターン3かパターン4かの判別ができていないので、この段階で『参照渡し(call-by-reference)』と決定づけることはできません。

パターン3かパターン4かを判別するためには、引数として与えられた配列(の参照型変数)の参照先を、関数内で変更した際の挙動を調べる必要があります。例えば以下のコードでテストできます。

function f(x) {
  x = new Array(1, 0, 0);
}

var a = new Array(0, 0, 0);
f(a);
console.log(a);

これを実行すると

[0, 0, 0]

と出力されるので、パターン3の『参照の値渡し』であることがわかります。

最後に

参考エントリでも言われていることですが、新しい言語を学習する際などは、『参照渡し(call-by-reference)』という記述を見ても鵜呑みにせず、『参照の値渡し』である可能性を疑ってテストコードで確認するのが無難かと思います。

また、自分で『参照渡し(call-by-reference)』に関して記述する場合は、「参照型変数を渡す」という意味で解釈されないように『call-by-reference』を併記したり、より詳細に『参照の参照渡し』などと表記するのが良いかと思ったのですが、面倒ですねこれ。

最後の最後に、『call-by-reference』を『参照渡し』と訳した人を全力で呪いましょう。

2011-12-28

Firefox9 + LDRize + Minibuffer + ReblogCommand で Tumblr reblog 環境構築

前提知識

  • ここ最近のFirefoxGreasemonkeyLDRize、Minibuffer、ReblogCommandをそのまま導入しても正しく動作しない
  • 正しく動作させるには、スクリプトを修正したり、以前のバージョンに差し替えたりする必要がある
  • 修正によって上手く動いたとしても、FirefoxGreasemonkeyの更新によってまた動かなくなることがある
  • その度に情報収集を行って再度修正する作業が非常に面倒臭い
  • 上記の理由により、自動更新機能をオフにして、古いバージョンのFirefoxやアドオンをずっと使い続けている人達が存在する

対象

  • 他のreblog環境も試してみたけどやっぱりFirefox + LDRize + Minibuffer + ReblogCommandが一番で
  • 動かなくなる度にいちいち修正するのが面倒だから古いバージョンのFirefoxやアドオンを使い続けていて
  • でもそろそろ新しいFirefoxにしたいと思っていて
  • 移行のための情報収集や試行錯誤が面倒だから『読んでその通りに実行すればいいだけの解説』が欲しい人

代替案

「j, k, tでreblogできさえすれば別に環境には拘らない」という人にはreblog machineをオススメします。登録するだけですぐ使えるので、自分で何かを導入する必要がありませんし、動作も高速です。自分も気分転換目的で時々使っています。

注意

ぶっちゃけ「ネットで情報収集してあれこれ試していたら動いちゃいました」レベルなので、作法的にマズイとか、セキュリティ的にマズイとか、そういった部分は調べていませんしわかりません。自己責任でお願いします。

手順

前置きが長くなりましたが、手順はわかってしまえば簡単です。Greasemonkey、Minibuffer、LDRizeの3つを修正する必要があります。

Add-on Compatibility Reporterの導入

Add-on Compatibility Reporter :: Add-ons for Firefox

からAdd-on Compatibility Reporterを導入します。Firefox9に対応していないバージョンのGreasemonkeyを修正して導入する必要があるため、そういった未対応の拡張機能を強制的に動作させる目的で使用します。導入するだけで特に設定は必要ありません。

Greasemonkeyの修正

Greasemonkey :: Versions :: Add-ons for Firefox

からGreasemonkey0.9.11を入手し、

Firefox 4 で Minibuffer + LDRize + ReblogCommand を動かす | WWW WATCH

を参考にして修正を行います。参考サイトではGreasemonkey 0.9.1を修正していますが、0.9.11でも同様の修正で対応できます。ただし、修正が必要となる行の番号は異なるので、"injectScripts"や"GM_registerMenuCommand"で検索をかけて該当箇所を探してください。

恐らく0.9.11〜0.9.11の範囲であれば同様の修正で対応できるかと思いますが、全部試したわけではないので何とも言えません。0.9.12移行は別の修正が必要らしいので避けましょう。

Minibufferの修正

Minibuffer for Greasemonkey

からMinibufferを入手し、

Firefox 4 で Minibuffer + LDRize + ReblogCommand を動かす | WWW WATCH

を参考にして修正を行います。

あとは全部入れるだけ

修正したGreasemonkey、Minibuffer、LDRizeをFirefox9にドラッグ&ドロップして導入します。

また、

/lang/javascript/userscripts/reblogcommand.user.js – CodeRepos::Share – Trac

からReblogCommandを、

tumblr Dashboard jk disable - Wescript

からtumblr Dashboard jk disableを導入します。

AutoPagerizeに関しては、

AutoPagerize for Greasemonkey

からユーザースクリプト版を導入するか、

AutoPager :: Add-ons for Firefox

から拡張機能版であるAutoPagerを導入するか、どちらか好きな方を選んでください。

これで完了です。

最後に

j, k, "p", tで快適なreblogライフを!