参照渡しと値渡しがスッキリわかった件【VueでToDoリスト】
Vue.jsでToDoリスト作成中です。
タスクを追加する際に二つのプロパティを持たせたオブジェクトを作成し、それを配列にpushすることでtodoリスト管理しようと思い以下のようにコードを書きました。
<html>
//一部抜粋//
<input type="text" v-model="todo">
<button @click = "addTodo">追加</button>
<script>
data:{
todo:{
comment: ''
status: '作業中'
},
todos:[],
},
methods:{
addTodo: function( ) {
todos.push(todo)
}
}
</script>
</html>
この場合次の新しいタスクを追加しようとinputに入力すると、既存のタスクまで書き換わってしまう現象が起こります。いわゆる「値渡し」「参照渡し」ってやつです。
それについて調べたんで書きます。
以下のコードをみて「???」な人向けの記事です。
//例1
var a = 10
b = a
b = 5
console.log(b) //5
console.log(a) //10
//例2
var a = { 10 }
b = a
b = { 9 }
console.log(b) // { 9 }
console.log(a) // { 9 }
例1みたいなのが値渡し。これはわかりやすいですよね。
例2は参照渡しです。b = { 9 }としたら、変数aまで変更されています。
これがなぜ起こっているのかというと、例①のb = a は変数aの値を渡していますが、例②のb = aは参照するメモリを渡しているからです。
なるほど、わからん。
メモリというのは「場所」です。
例①の場合のb = aは10という数字(値)を渡してますが
例②の場合のb = aは場所であるメモリ35174(適当な数字です)を参照してね!といっているんです。
そしてメモリ35174という場所に{ 10 }が入っている。
その場所にある{ 10 }を参照しているから変数aも変数bも{ 10 }になります。
でも、変数bがメモリ35174にある{ 10 }を{ 9 }に変更しました。
そうすると変数aもメモリ35174を参照しているわけですから、コンソールしてみると{ 9 }となっているわけです。
JavaScriptにおける値渡しと参照渡しの切り分け
じゃあ、どんなときに参照渡しになるの?って思いますよね。
結論からいうとJavaScriptにおいてプリミティブ型は値渡し、オブジェクト型は参照渡しになります。
プリミティブ型というのは具体的には次の5つのことです。
数値型
文字列型
ブーリアン(boolean)型
null型
undefined型
コレ全部覚えるのは大変なんで(5個くらい覚えろやって言われそうですが)僕はざっくりこんな風に理解してます。
①プリミティブは「原初の」とか「素朴な」という意味。料理でいうと人参とか肉といった材料のようなイメージ、逆にオブジェクト型はそういった材料を使って作る料理のイメージ
②っていうかオブジェクト型ってJavaScriptの場合はオブジェクトと配列しかないんで、それ以外はプリミティブ型。
ここまで理解すると冒頭の「???」が解決します。
//例1
var a = 10 //これは数値型。
b = a //数値だから値渡し。
b = 5 //変数bの値を5に変更。
console.log(b) //5
console.log(a) //10
//例2
var a = { 10 } //オブジェクト型だから参照渡し
b = a //意味「変数bも変数aと同じメモリを参照しろよ!」
b = { 9 } //変数b「メモリ(参照している場所)の{ 10 }を{ 9 }に変えとくわ。」
console.log(b) // { 9 }
console.log(a) // { 9 }
って感じなので、元の僕のコードもオブジェクトを渡してしまっているところを書き換えるとうまくいきます。
変更前
<html>
//一部抜粋//
<input type="text" v-model="todo">
<button @click = "addTodo">追加</button>
<script>
data:{
todo:{
comment: ''
status: '作業中' //オブジェクト渡してしまっているのが原因。
},
todos:[],
},
methods:{
addTodo: function( ) {
todos.push(todo)
}
}
</script>
</html>
変更後
<html>
<input type="text" v-model="todo">
<button @click = "addTodo">追加</button>
<script>
data:{
newComment: '' ,
todos:[],
},
methods:{
addTodo: function( ) {
todos.push({status: '作業中', comment: ' this.newComment'})
}
}
</script>
</html>
まとめ
JavaScriptにおいて、オブジェクト型(配列とオブジェクト)は参照渡しになる。それが嫌ならプリミティブ型に変更しましょう。
でも、実はJavaScriptは参照渡しも値渡しもないんです。このあたりの細かい話は以下の参考記事がお勧めです。
参考記事
【JS】いかがでしたか!?値渡しと参照渡しの使い分け!?
3歳娘「パパ、もう参照渡しの話はやめて?」
JavaScriptに参照渡し/値渡しなど存在しない