JavaScriptのオブジェクトを扱うとき、=は値渡しではない
2014-09-06の記事が検索で引っかかるようになっていたので、元記事にいただいていた指摘を元に修正しました。
修正後はこちら。
JavaScriptのオブジェクトを扱うとき、=はただの値渡しではない - woshidan's blog
こちらは自戒用のアーカイブです。初心者が似たようなところで勘違いしていたらこう言う馬鹿がいたけど、多少はマシになったから大丈夫だよ、という話等にお使いください。
以下、アーカイブ。
今日の昼間、ちょちょいのちょいだぜー、と思っていた処理の実装がこれが原因で30分以上詰んだので、切なくなったから書く。
参考にしたのはここらへん。
JavaScript でオブジェクトや配列をコピーする方法 | monopocket.jp
JavaScript のオブジェクトは参照渡しで代入される | monopocket.jp
javascript - What is the most efficient way to clone an object? - Stack Overflow
=で値渡しされないの?
JavaScriptの場合、オブジェクトだと値渡しされない。
値渡しというのは不正確でオブジェクトのdeep copy(同一のインスタンスを新しく生成して、そのインスタンスのアドレスを渡す)が、オブジェクトの場合なされない。
参照渡しにあたるのはshallow copy(=の右辺のオブジェクトのアドレスを渡す)というらしい。
オブジェクトを右辺において=を使うとshallow copyが行われる。
値渡し、参照渡しを特に意識するのはC言語くらいで、それも値渡しをするには&とか特別な記号が必要だと思っていたので驚いた。
で、=で値渡しをされないオブジェクトですが、
いくつか忘れたとき用にオブジェクトの例を書いておくと、
- 日付,時間(Date,Timeオブジェクト)
- 配列,ハッシュ(=オブジェクト)
- 自分で作成したオブジェクト
例
で、
大丈夫なの(要するにただの変数)は
- 数字
- 文字列
- 真偽値
例
deep_copyをするにはどうするか
shallow_copyになってるから気をつけようね、じゃなくて値渡しdeep_copyがしたい!
という場合どうしたらいいか。
元オブジェクトの値を初期値とする新しいインスタンスを直接作る
コピーとはなんだったのか。
まあ、ガンバるのはブラウザだから、まあ……。
JSON.parse(JSON.stringify(obj))を使う
文字列が値渡しできるならとりあえず一回文字列にしちゃえばいいじゃない。
ただし、Dateオブジェクトはこれだとうまくいかない。
Jqueryの$.extend()を使う
既にJqueryを使っているなら、これが一番楽?
true => deep copyをする, false => shallow copyをする、の指定で、
これは空のオブジェクトにoriginal_dateのプロパティを追加している感じ。
でも同じ。こちらはoriginal_dateに空のオブジェクトのプロパティを追加という意味。
でもいけた。
これは空のオブジェクトを生成して、original_dataに空のオブジェクトのプロパティを追加したものを代入して返す、という意味。
自分には少し分かりにくかったので、例。
日付については、
というふうにこっちでも上手く行かなかったので、とりあえず、
がよさそう。
最近になって、なぜか妙にstubの書き方が少しだけ身にしみたのだけど、
使用するモデルの切り分けがうまく出来てなくて、結局ほとんど活用できていない感。
追記
shallow_copyではなく参照の値渡しです 両者の挙動は違います。
という指摘をいただきました。
配列などのオブジェクトで代入演算子の=を使うと、アドレスが渡されるのではなく、
アドレスの値が入った変数がコピーされる(参照の値渡し)ようです。
詳しくは、
Rubyist Magazine - 値渡しと参照渡しの違いを理解する
の参照の値渡しの項が分かりやすかったです。
というか、Rubyでも行われるのか。気をつけよう。
書いててもやもやしていた部分がすっきりしました。
jserさん、ありがとうございました。