🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

4回答

783閲覧

javascriptでのclass使用について

kyskgsh

総合スコア1

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2021/01/17 03:59

編集2021/01/17 05:34

疑問

【追記】
分かりにくいとの指摘を受けておりますので、コード補記しました。


javascriptでclassの勉強をしていますが、下記のコードを実行した際、
「q.obj.x」が加算されていきます。

class内のmethodでは、別のオブジェクトを作成し、その値を加算しているつもりなのですが、
どこに問題があるのでしょうか?

アニメーションの中で初期値を保持しながら、初期値からの変動で動きを表現したいのですが。

javascript

javascript

1class t{ 2 constructor(){ 3 this.obj = {x:0, y:0};//こちらが初期値です。例えばプレイヤーの座標。 4 } 5 add(moveFlg){ 6 var a = {}; 7 a.obj = this.obj; 8 if(moveFlg == 1){ a.obj.x++; }//xの初期値(0)に1を加算したい。yは初期値のままでよい。 9 else if(moveFlg == 2){ a.obj.x--; }//xの初期値(0)から1を減算したい。yは初期値のままでよい。 10 //moveFlgが0の時は初期値{x:0, y:0}にしたい 11 return a; 12 } 13 add2(){ 14 return this.obj.x++;//こちらだとxにどんどん加算されていくのは理解できる 15 } 16 render(moveFlg){ 17 var A = this.add(moveFlg); 18 //A.obj.x(1 or -1)とA.obj.y(0)をもとに描画する処理 19 } 20} 21 22var q; 23var moveFlg; 24window.onload = function(){ 25 q = new t();//こちらでプレーヤーの宣言 26 init(); 27}; 28 29function init(){ 30 document.addEventListener('keydown', KeyDownFunc); 31 document.addEventListener('keyup', KeyUpFunc); 32 requestAnimationFrame(update); 33} 34 35function update(){ 36 q.render(moveFlg);//プレイヤーに動きを加え描画 37 requestAnimationFrame(update); 38} 39 40function KeyDownFunc(e){ 41 switch(e.keyCode){ 42 case 87: 43 moveFlg = 2; 44 break; 45 case 83: 46 moveFlg = 1; 47 break; 48 } 49} 50 51function KeyUpFunc(e){ 52 moveFlg = 0; 53}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2021/01/17 04:45

a.obj = this.obj; としているのでそうなって当然というか、そもそも何がしたいコードなのか分かりません。
退会済みユーザー

退会済みユーザー

2021/01/17 04:50

こんにちは。質問文から何に答えて欲しいのか分からないのですが、どの値がどう変わると理想なんでしょうか? って事で質問の修正をお願いします。 不明なキーワード: アニメーション → requestAnimationFrame関数の事? 初期値 → 具体的な数値と何行目の処理を言っているのか? requestAnimationFrame関数に初期値っぽいのないし。 初期値からの変動 → q.obj.x を加工した何かなんだろうけどハッキリしない そのまま実行すると、consoleにPC能力全開で1ずつカウントアップしながら q.obj.x の中身が流れ続けます。 アニメーションのフレーム(時間的な意味合い)に使えそうではありますが。 とりあえず、こういうのはタイマー処理した方がブラウザやPCフリーズとか回避できるんで検討してください。
guest

回答4

0

class内のmethodでは、別のオブジェクトを作成し、その値を加算しているつもりなのですが

つもりでしかありません。a.obj = this.obj;とした場合、両者は同じオブジェクトを指します。

投稿2021/01/17 05:40

maisumakun

総合スコア145973

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

参照型

原因は既存回答の通り、

JavaScript

1a.obj = this.obj;

Object型(参照型)の代入が参照値の代入として扱われる為です。
Object型の値複製をする場合は、プリミティブ値単位でコピーしなければなりません。

修正コード

下記コードは質問のコードを維持して修正したものですが、

JavaScript

1'use strict'; 2class Player { 3 moveX (xDirection) { 4 const currentPoint = Object.assign({}, this.defaultPoint); // シャローコピー 5 6 switch (xDirection) { 7 case 1: 8 currentPoint.x++; 9 break; 10 case 2: 11 currentPoint.x--; 12 break; 13 } 14 15 return currentPoint; 16 } 17 moveY (yDirection) { 18 const currentPoint = Object.assign({}, this.defaultPoint); // シャローコピー 19 20 switch (yDirection) { 21 case 1: 22 currentPoint.y++; 23 break; 24 case 2: 25 currentPoint.y--; 26 break; 27 } 28 29 return currentPoint; 30 } 31 render (xDirection, yDirection) { 32 const currentPoint = {x: this.moveX(xDirection).x, y: this.moveY(yDirection).y}; // 座標オブジェクトがthis値に存在しない為、返り値から不要な値を除去して合成する 33 console.log(currentPoint); 34 // 描画処理 35 } 36} 37 38Object.defineProperty(Player.prototype, 'defaultPoint', { // 初期座標は固定の為、[[Prototype]] 上に保存しておく 39 enumerable: true, 40 value: {x: 0, y: 0} 41}); 42 43new Player().render(); // {x: 0, y: 0} 44new Player().render(1,2); // {x: 1, y: -1} 45new Player().render(2,1); // {x: -1, y: 1}

render() メソッドで座標オブジェクトを合成する処理に無駄があります。
moveX() ではY座標を破棄し、moveY() ではX座標を破棄しており、各メソッドで無駄なデータが存在しています。
解決手段として、現在座標をインスタンスプロパティに持っておく実装が考えられます。

JavaScript

1'use strict'; 2class Player { 3 constructor () { 4 this.currentPoint = Object.assign({}, this.defaultPoint); 5 } 6 moveX (xDirection) { 7 switch (xDirection) { 8 case 1: 9 this.currentPoint.x++; 10 break; 11 case 2: 12 this.currentPoint.x--; 13 break; 14 } 15 } 16 moveY (yDirection) { 17 switch (yDirection) { 18 case 1: 19 this.currentPoint.y++; 20 break; 21 case 2: 22 this.currentPoint.y--; 23 break; 24 } 25 } 26 render (xDirection, yDirection) { 27 this.moveX(xDirection); 28 this.moveY(yDirection); 29 console.log(this.currentPoint); 30 // 描画処理 31 } 32} 33 34Object.defineProperty(Player.prototype, 'defaultPoint', { 35 enumerable: true, 36 value: {x: 0, y: 0} 37}); 38 39new Player().render(); // {x: 0, y: 0} 40new Player().render(1,2); // {x: 1, y: -1} 41new Player().render(2,1); // {x: -1, y: 1}

変数名/プロパティ名の付け方

変数名/プロパティ名のつけ方がいい加減なので、サンプルコードだとしても意味のある名前にすべきかと思います。

  • add() なのに減算がある
  • 初期座標なのに obj という名前

意味を考えて命名することで思考が整理され、無駄なコードを書かずにすみます。

参照の値渡し(通称)

「参照渡し」、理解致しました。

Object型(参照型)には、変数に再代入された際に参照元にも再代入値を反映する「参照渡し」の性質がありません。

JavaScript

1let obj1 = {}, obj2 = obj1; 2 3obj2 = null; 4console.log(obj1 === obj2); // false 5console.log(obj1, obj2); // {} null

オブジェクトのプロパティ書き換え時に参照元に反映する性質は「参照の値渡し」または「共有渡し」のように表現されることがあり、「参照渡し」と区別されています。
(※「~渡し」はES2020仕様に存在しない用語であり、あくまでも説明の為の便宜上の言葉です。標準的な用語ではありません。)

JavaScript

1let obj3 = {}, obj4 = obj1; 2 3obj3.x = 1; 4console.log(obj3 === obj4); // true 5console.log(obj3, obj4); // {x: 1} {x: 1}

参照渡し

違いを認識するには「参照渡し」の概念を先に理解する必要があると思います。

「参照」とは「Windowsのショートカットファイル」「Linuxのエイリアス」のようなものです。

もしも、JavaScriptでPHPの参照渡しが使えたならば、次の結果になったはずです。

JavaScript

1let obj5 = {}; 2let obj6 = &obj5; // 参照渡しされるものと仮定する 3obj6 = null; 4console.log(obj5 === obj6); // true 5console.log(obj5, obj6); // null null

obj6obj5 の「参照」なので、obj6 を参照する時、それは obj5 への参照として扱われます。

JavaScript

1obj6 = null;

つまり、上記コードは下記コードと等価です(逆もしかり)。

JavaScript

1obj5 = null;

現在のJavaScript(ES2020)は「値渡し」しか出来ない為、このような機能は実現できません。

なお、本回答で「参照値」と表現したのは「参照を持つ値」という意味です。
同じ「参照を持つ値」を代入すればプロパティ書き換えはお互いに影響しあいますが、再代入すると別の値に変わるので、同じ参照を持たなくなります。
参照渡しならば、再代入時に参照先が再代入されるので、どんな場合でも完全に同一になります。

Re: kyskgsh さん

投稿2021/01/17 07:06

編集2021/01/17 16:55
think49

総合スコア18189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kyskgsh

2021/01/17 07:22

非常に詳細なご説明有難うございました。 参考サイトも閲覧させて頂きました。 「参照渡し」、理解致しました。
miyabi_takatsuk

2021/01/17 08:37

横槍失礼します。 質問者さん、 JavaScriptには「参照渡し」は存在しません。 似て非なるものです。 なので、ご回答では、 > 参照値の代入として扱われる為 と、違う言葉を使われています。
think49

2021/01/17 11:20

回答に補足しておきました。 >参照の値渡し Primitive型は「値渡し」といわれますが、実は両方とも「参照の値渡し」でPrimitive型にプロパティ書き換えの手段がないが為にただの「値渡し」のように振舞っていると見るのが正解な気がします。
think49

2021/01/17 11:47

> Primitive型にプロパティ書き換えの手段がないが為に 「Primitive型にプロパティがない」がより正確ですかね。 > 実は両方とも「参照の値渡し」 これについては、@jserさんが近いニュアンスで回答しているのを見つけました。 https://teratail.com/questions/14541#reply-22419
miyabi_takatsuk

2021/01/17 13:13

> 「値渡し」のように振舞っていると見るのが正解な気がします。 > > 実は両方とも「参照の値渡し」 これが黄金のような気がします。 私は、 常に変数は、代入された値に対する参照となり、 参照自体を渡しているわけではない、 それはオブジェクトだろうが、プリミティブだろうが関係なく、 変数に変数を代入した場合は、 メモリに格納された元の値(オブジェクトも含む)の新たな参照になる(新たに宣言・代入された変数が) だけである、と解釈しています。 ソースも根拠も出せませんが、、、 ただ、様々勉強し、試してみてる限りではそうだと思っています。
kyskgsh

2021/01/17 13:30

皆様、補足のご解説有難うございます。 かなり難しいですね。 「a=b」は「参照の値渡し」であるが、Primitive型(文字列、数値、BigInt、真偽値、undefined、シンボル)の場合「プロパティがない(メソッドを持たない)」為、aを変更してもbは変わらない。 逆にPrimitive型でない場合は、「プロパティがある(メソッドを持つ)」為、aを変更するということは、bを変更する事になる。 という程度の理解です。 一度、 move.obj = Object.assign({}, this.obj); を試してみたのですが、結局、元の挙動と変わらない挙動となりましたので、 一旦、仮に move.obj = JSON.parse(JSON.stringify(this.obj)); としています。 皆様のご回答を参考にさせて頂き、コードのブラッシュアップを図るようにいたします。
miyabi_takatsuk

2021/01/17 13:38

> aを変更するということは、bを変更する事になる。 という程度の理解です。 ここの理解が大事かと。 あくまで、aとbは、同じオブジェクトを参照しているだけで、 宣言後は両者に関係性はありません。 参照元のオブジェクトが変化しているだけです。 まぁ、大事ではありますが、今そこを深く考え出すと沼るような気はしますので、 使い方と、挙動を覚える、程度にした方がいいかもですね。
think49

2021/01/17 15:09 編集

@miyabi_takatsuk さん > メモリに格納された元の値(オブジェクトも含む)の新たな参照になる(新たに宣言・代入された変数が) そうですね。 参照渡しは再代入しても参照性が失われませんが、値渡しは再代入時に「別の参照を持つ値」に変わる部分が重要な違いですね。 同期が取れるのは「同じ参照を持つ値」を各々の変数に代入した場合ですが、この点を突き詰めて説明した資料は少ないように思います。 # 感覚的には変数値とは別の場所に「オブジェクト」があり、各々の変数値が該当オブジェクトを参照しているイメージです。 # 参照そのものではないので、再代入には対応できないわけですね。 --- @kyskgsh さん 回答に「参照渡し」を追記しました。 @miyabi_takatsuk さんが指摘されているように、別言語で「参照渡し」「値渡し」の概念を学習すると良いと思います。
guest

0

a.obj = this.obj によって、this.obj の参照がコピーされているからですが、
初期値が{x:0 ,y: 0} で変わらないのなら、わざわざtに状態として保持させておく必要もないんじゃないでしょうか。

javascript

1 2 add(moveFlg){ 3 var a = {obj: {x: 0, y:0}}; 4 if(moveFlg == 1){ a.obj.x++; }//xの初期値(0)に1を加算したい。yは初期値のままでよい。 5 else if(moveFlg == 2){ a.obj.x--; }//xの初期値(0)から1を減算したい。yは初期値のままでよい。 6 //moveFlgが0の時は初期値{x:0, y:0}にしたい 7 return a; 8 } 9

ただ、やりたいのが「キーアップのタイミングで初期値に戻る」ということなら、

javascript

1 add(moveFlg){ 2 if (moveFlg == 0) { this.obj = {x: 0, y: 0}; } 3 else if(moveFlg == 1){ this.obj.x++; } 4 else if(moveFlg == 2){ this.obj.x--; } 5 return this; 6 } 7

というのはどうですか?

※参照渡し、値渡しについて調べてみると良いと思います。

投稿2021/01/17 06:13

編集2021/01/17 14:43
umau

総合スコア831

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kyskgsh

2021/01/17 07:23

ご回答ありがとうございました。 「参照渡し」ですね。 了解いたしました。
m.ts10806

2021/01/17 09:01

あまり、昔の記事(しかも個人が書いた記事)を根拠とするのは危険かと思います。
miyabi_takatsuk

2021/01/17 10:44

> this.obj の参照がコピーされているからですが、 厳密に言えば、コピーではありません。 a.objが、同じオブジェクトに対しての、新たな参照となっているだけです。 故に参考で出された記事は間違っていると言わざるをえません。 JavaScriptには値渡しも参照渡しもありませんので。 実際のC++などの参照渡しに関して勉強したらわかりますが、 両者は似て非なる仕様です。
umau

2021/01/17 14:42

レスありがとうございます! 記事はすいません、ググってザっと見で貼りました、、 コード実行して結果表示して説明されてたので、実用上問題ないかと思ったんですが、もう少し内容吟味するように気をつけます。 >m.ts10806さん 記事へのリンクは削除しておきます! >miyabi_takatsukさん 理解が間違っていたら申し訳ないのですが、今のところ私の頭の中は「全て参照(そのもの)を値としてコピーしている」という理解になってます。アドレス(という言い方を使っていいのか分からないですが)がコピーされるイメージです。a.obj = this.obj は、this.obj を指すアドレスがコピーされると理解してます。数値などのリテラルも内部では全てオブジェクトで、変数にはその参照が入り、数値が計算に使われた時は結果から新たな数値オブジェクトが生成されてそのアドレスが変数に入る、と理解しています。言われるところの、「新たな参照となっているだけ」というのは、同じ意味と思っても大丈夫でしょうか?
miyabi_takatsuk

2021/01/17 14:54 編集

a.objが、this.objが指すオブジェクト自体の参照になる、であって、 this.objの参照をコピーして、a.objに入れている、ではありません。 あくまで参照対値の関係なので。 =は"入れる"ではなく、"代入"のため、 右項の式が返した値を左項の変数が参照しているだけです。 なので、コピーとは違うんですよ。 (もちろんコピーと考えた方が理解はしやすいかもしれません) よって、参照渡し、は行っていません。 参照渡しに関して、JavaScript 参照渡し と調べたとしても、 間違った解釈の記事しか散見されないと思います。 (JavaScriptの参照渡しは〜って内容の記事)
umau

2021/01/17 15:15

厳密には違う、ということですか、、物理的にも(参照そのものを)コピーしているという理解の仕方をしておりました。さっきから頭を巡らせてみてますが、正直なかなかイメージがしっくりこないです。難しいですね。参照というものの実体について、あくまで想像による概念としての理解にとどまっているからなんですが、、ともかく実体としてはコピーではないというのは覚えておこうと思います。ありがとうございます!
miyabi_takatsuk

2021/01/17 15:58 編集

解釈が参考になったなら幸いです。 私も、JSの参照に関して、勘違いをずっとしていましたが、 teratailでみなさんからご指摘いただいて、理解できたクチです。 > 難しいですね。 理解できると、あぁなるほど、ってなるんですが、 難しいとは思います 汗 JavaScriptは、参照渡しではない、参照渡しとは言わない、 という観点から考察し、いろいろ処理を書いたり、調べていくと、理解に近くと思います。
guest

0

参照と値の違いです

class内のmethodでは、別のオブジェクトを作成し、その値を加算しているつもり

JavaScript

1var a = {}; 2a.obj = this.obj;

この部分で、aという「別のオブジェクト」を生成しているという認識だとおもいますが、これはthis.objを"参照"しているだけになります。
ですので、コード自体は正しく動いていると言えます。

このような仕様を「参照渡し」とか「値渡し」といったりすることがあります。
こちらの記事を参考にしてみてください。
https://qiita.com/migi/items/3417c2de685c368faab1

ちなみに、「初期値」と「初期値に加算したい値」がある場合は、それぞれ別の変数を用意したほうが良いです。

また実際にコピーしたい場合はどうするのかというと、このようなObject.assign()を使用すればコピーすることは出来ます。
https://arrown-blog.com/js-object-assign/
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

ただし、ただ単にコピーしたいだけの場合はよく考えて使わないと、見通しがかなり悪いコードになってしまうのでご注意ください。

投稿2021/01/17 06:04

編集2021/01/17 06:10
mattari_panda

総合スコア429

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi_takatsuk

2021/01/17 08:48

> このような仕様を「参照渡し」とか「値渡し」といったりすることがあります。 確かに言うことはありますが、 間違っています。(そう言うこと自体が) JavaScriptにおいては、参照渡しも値渡しも存在しません。 変数は、あくまで、メモリに保持された値に対する参照に過ぎません。 他回答者さんのおっしゃる通り、 メモリに保持された同じオブジェクトに対しての参照になっているだけです。 渡すのではなく、変数自体が参照なので、渡しようがありません。 参照渡し自体は、他の言語において、参照渡しとしての仕様があります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問