質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

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

Q&A

解決済

1回答

1731閲覧

「bind」や「call」によるthisの固定を一回一回設定しないようにしたい。

meron-pan

総合スコア44

JavaScript

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

0グッド

0クリップ

投稿2018/10/05 14:11

編集2018/10/05 16:32

前提・実現したいこと

ECMascript5水準で、本格的なクラスを作ってみようと思い、初めて作ってみたコードを実験してみたところ

JavaScript

1AAA = function (i, j, k) { 2 var i = i || 0; 3 var j = j || 1; 4 var k = k || 2; 5 6 var p = AAA.prototype; 7 p.out = function () { 8 console.log(i + j + k) 9 } 10} 11 12bbb = new AAA(3,4,5); 13ccc = new AAA(6,7,8); 14 15bbb.out();//21 16ccc.out();//21 17

このように、どちらも同じ結果になってしまうことが判明し、クロージャーを捨てて全ての変数を「this」で書き直して、out関数に引数を追加しました。また、thisの参照位置がクラスと関数で異なってしまうので、さらに「bind」と「call」を用いて出力まで可能にしたコードが以下のコードです。

JavaScript

1AAA = function (i, j, k) { 2 this.i = i || 0; 3 this.j = j || 1; 4 this.k = k || 2; 5 6 var p = AAA.prototype; 7 p.out = function (l) { 8 console.log(this.i + this.j + this.k + l) 9 } 10} 11 12bbb = new AAA(3,4,5); 13ccc = new AAA(6,7,8); 14 15//bindで実行 16NEObbb = bbb.out.bind(bbb); 17NEOccc = ccc.out.bind(ccc); 18NEObbb(1);//13 19NEOccc(1);//22 20 21//callで実行 22bbb.out.call(bbb,1);//13 23bbb.out.call(ccc,1);//22 24

これでも実際動くので、文法上の問題はないのですが、
「bind」と「call」を使ったやり方では、どちらも冗長な書き方になってしまいます。
「bind」の場合は、Function.prototype.bind() - JavaScript | MDN - Mozillaによると新しい関数を生成するそうなので、せっかくのプロトタイプ宣言(メモリ節約のつもり)が無駄になってしまいますし、「call」を使ったやり方も当初予定していた関数の後ろに「call(クラス名)」を必ず付けなくてはいけなくなってしまうので、それはそれで面倒です。
なんとか、見やすい記述になるように、

JavaScript

1bbb.out();//これで出力

できるようにしたいのですが、方法はないでしょうか?

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

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

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

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

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

guest

回答1

0

ベストアンサー

prototype-chain

「なぜ bind, call を使うのか」が質問文から読み取れませんでしたが、問題の根本的原因は「AAA.prototype の参照が各インスタンスで共有している」というプロトタイプの原理を理解できていない事にあるように思います。

作り方は人それぞれポリシーがあるでしょうが、素直にES5で書くならば、こんな感じです。

JavaScript

1'use strcit'; 2function AAA (i, j, k) { 3 var len = arguments.length; 4 5 this.i = len > 0 ? +i : 0; 6 this.j = len > 1 ? +j : 1; 7 this.k = len > 2 ? +k : 2; 8} 9 10Object.defineProperty(AAA.prototype, 'out', { 11 writable: true, 12 enumerable: false, 13 configurable: true, 14 value: function out () { 15 console.log(this.i + this.j + this.k); 16 } 17}); 18 19new AAA(3,4,5).out(); 20new AAA(6,7,8).out(); 21 22var out = AAA.prototype.out.bind({i:0,j:1,k:2}); 23 24out(); 25AAA.prototype.out.call({i:1,j:2,k:3});

引数なしの判定

JavaScript

1this.i = i || 0; 2this.j = j || 1; 3this.k = k || 2;

i, j, k が 0 である場合に右辺の初期値が適用されます。
引数未指定時を正確に判定したい場合は、arguments.length を使用します。

Re: meron-pan さん

投稿2018/10/05 14:49

think49

総合スコア18162

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

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

meron-pan

2018/10/05 16:27

bbb.out()を普通に実行すると、NaNが出力されるのでcall,bindを使って、thisを固定させました。 私の中ではprototypeにすると、生成したインスタンスが持っていないメソッドを使っても、prototypeを辿ってメソッドを実行するという認識です。なので、this.method = function()...と逐一メソッドを作るよりも、処理内容が同じならメモリを使わなくて済むんだな・・・くらいの感じで使っているのですが、間違っているのでしょうか?
think49

2018/10/06 01:34 編集

> bbb.out()を普通に実行すると、NaNが出力されるのでcall,bindを使って、thisを固定させました。 「普通に実行する」の再現コードはどれでしょうか。 質問文の2番目のコードの事なら、「引数未指定⇒l(undefined)を加算⇒NaN出力」が原因です。 bind時のコードと同じように、bbb.out(0) で期待通りに動作するはずです。 ちなみに、「引数無し判定のコード例」を親記事で触れています。 > this.method = function()...と逐一メソッドを作るよりも、処理内容が同じならメモリを使わなくて済むんだな prototypeはメモリ節約になりますが、コンストラクタ実行の度に、毎回同じ prototype を定義するのは無駄だと思います。 質問文の1番目のコードに至っては、コンストラクタ実行の度に「違うprototypeを定義している」ので、prototypeの原理を理解しておられないように読めました。 原理は親記事に書いた参考サイトを読んでください。
meron-pan

2018/10/06 04:10

>質問文の2番目のコードの事なら、「引数未指定⇒l(undefined)を加算⇒NaN出力」が原因です。 bind時のコードと同じように、bbb.out(0) で期待通りに動作するはずです。 スイマセン。self = this;としないと動かなくなるコードのつもりで書いたつもりだったんですが、普通にこれ動作しますね・・・。
meron-pan

2018/10/06 04:33

NaNが出てしまう理由も引数の指定ミスですし、お恥ずかしい限りです。出直してきます。
think49

2018/10/06 04:43 編集

解決して良かったですね。 this値の扱い、プロトタイプチェーンについては学習し直すと、良い気がします。 ところで、親記事に書いた「引数なしの判定」は質問文のコードのままで期待通りの動作だったのでしょうか。 私としては、new AAA(0,0,0).out(0) の結果が直観的ではないのですが。
meron-pan

2018/10/06 15:08

そうですね、webページにちょこちょこっと動きを加えるだけならできたんですけど、こうやってクラスとか作ろうとするとやっぱり勉強しなきゃいけないことが多そうです(ECMascript2015が登場しているのでそろそろ使われなくなりそうなやり方ではありますが、まだまだclass構文もシンタックスシュガーですし)。 後、引数の件ですが、0以外の数値を用いるときには、引数が必要になればいいので特には問題ないです。
meron-pan

2018/10/06 15:15

ただ、||を引数が指定されなかった時用に使ったのは最近のことでして(それまでは、if文で書いてました)、この演算子の挙動までは深く考えていなかったので、勉強になりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問