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

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

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

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

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

Q&A

解決済

1回答

1621閲覧

コールバックの無名関数を名前付きの関数に置換したい

tara-tail

総合スコア32

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

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

0グッド

0クリップ

投稿2019/08/31 06:46

編集2019/08/31 09:29

###やりたいこと
コールバック部分の無名関数を、名前付きの関数に置換したいと考えています。

###元のコード
<products.js>

js

1const database = [ 2 { id: 1, name: '商品A', price: 100, content: '商品A詳細' }, 3 { id: 2, name: '商品B', price: 200, content: '商品B詳細' }, 4 { id: 3, name: '商品C', price: 300, content: '商品C詳細' } 5] 6 7export default { 8 asyncFind(id, callback) { 9 setTimeout(() => { 10 callback(database.find(el => el.id === id)) 11 }, 1000) 12 }

<Product.vue>

vue

1<script> 2 import products from 'products.js' 3 export default { 4 props: { 5 id: Number 6 }, 7 data() { 8 return { 9 item: null 10 } 11 }, 12 watch: { 13 id: { 14 handler() { 15 products.asyncFind(this.id, item => { 16 this.item = item 17 }) 18 }, 19 immediate: true 20 } 21 } 22 } 23</script>

###やってみたこと
<products.js> に以下のfind関数を追記し、asyncFindからcallbackを削除し、find関数を配置しました。これで、無名関数を使用していた時と同じように動くと思いましたが、<Product.vue> で商品名などが表示されませんでした。
<products.js>

js

1export default { 2 find(id) { // 追記した関数 3 return database.find(el => el.id === id) 4 }, 5 asyncFind(id) { 6 setTimeout(() => { 7 return this.find(id) // callbackを削除してfind関数を配置 8 }, 1000) 9} 10

<Product.vue>

vue

1<script> 2 import products from 'products.js' 3 export default { 4 props: { 5 id: Number 6 }, 7 data() { 8 return { 9 item: null 10 } 11 }, 12 watch: { 13 id: { 14 handler() { 15 // ここを書き換えて、asyncFindからの戻り値を、`this.item`に格納しようとした 16 this.item = products.asyncFind(this.id) 17 }, 18 immediate: true 19 } 20 } 21 } 22</script>

###質問
そもそも、<products.js> の無名関数部分を、名前付きの関数に置換するのが間違っていたのか、それとも私のコードの考え方・書き方に間違えがあるのか、ご指摘していただけると助かります。

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

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

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

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

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

Lhankor_Mhy

2019/08/31 07:07 編集

this.find(id) ↑この this には何が入っているのですか? アロー関数は this を束縛しないので、ご提示の部分だけではわからないと思うのですが。 また、 asyncFind(id, callback) {} の第2引数が削除されていますが、これは、this.findと同じものなのですか?
Lhankor_Mhy

2019/08/31 07:12

あ、Product.vue も書き換えているのですね。失礼しました。 そうすると、その部分の this が一致するのかが気になりますね。
tara-tail

2019/08/31 07:17

Lhankor_Mhyさん、ありがとうございます。私がthisの使い方を間違っていると思います。ここの場合は、アロー関数内なので、thisはwindowオブジェクトを参照してしまいますよね?コードを書き換えますので、再度ご確認いただき、お手数ですが再度ご指摘ください。
tara-tail

2019/08/31 07:21

thisは、find関数を指しているつもりです。もしかしたら、不要なのかもしれません。
Lhankor_Mhy

2019/08/31 07:42

this.item = products.asyncFind(this.id) とありますが、これは products.asyncFind の戻り値を this.item に代入するということになるかと思います。 一方で、asyncFind には return 文がありませんから、undefined を返すと思います。 そこに問題がありそうな気がしますが……
tara-tail

2019/08/31 08:27

Lhankor_Mhy様。find関数にreturnがあるので、てっきりasyncFindからも値が戻ると思っていたのですが、勘違いだったようです。 asyncFindから、ちゃんと値が戻るようにするにはどうしたら(どこにreturnを書けば)良いでしょうか?たびたび申し訳ありません。
Lhankor_Mhy

2019/08/31 08:37

非同期なので、handler の中で代入するには、async await で書くしかないと思いますが…… asyncFind に引数で this を渡して、そちら側で代入するのではダメなのですか?
tara-tail

2019/08/31 08:43

Lhankor_Mhyさん、ありがとうございます。ちょっと私の頭では理解できておりません。thisを渡すというのは、<products.js> のasyncFind の引数に「asyncFind (id, this)」のようにするのでしょうか?
Lhankor_Mhy

2019/08/31 08:53

そうですね。そちら側の this と一致するなら必要ないですが、おそらく異なるのではないですか?
tara-tail

2019/08/31 09:32

Lhankor_Mhyさん、<Product.vue> の「this.item」に値を渡すには、callback関数でないと難しいみたいですね。諦めて、元のコードに戻そうと思います。ありがとうございました。
Lhankor_Mhy

2019/08/31 09:57

お力になれずすみません。 目的に合うのかわかりませんが、callback を残していいなら、 find(id) { // 追記した関数 return database.find(el => el.id === id) }, asyncFind(id, callback) { setTimeout(() => { callback( this.find(id) ) }, 1000) } でもいいのかもしれませんね。
guest

回答1

0

ベストアンサー

修正後のproducts.asyncFind関数はundefinedのみしか返していません。さて、これを修正して動作するようにする方策はいくつかあります。そのうちのひとつを以下に示してみます。

javascript

1export default { 2 find(id) { // 追記した関数 3 return database.find(el => el.id === id) 4 }, 5 asyncFind(id) { 6 return new Promise(res => setTimeout(() => { 7 res(this.find(id)) 8 }, 1000)) 9 } 10}

まずこのようにすることで、asyncFind関数がpromiseを返すようになります。値を受ける側は、このようにします:

vue

1<script> 2 import products from 'products.js' 3 export default { 4 props: { 5 id: Number 6 }, 7 data() { 8 return { 9 item: null 10 } 11 }, 12 watch: { 13 id: { 14 handler() { 15 products 16 .asyncFind(this.id) 17 .then(it => this.item = it); 18 }, 19 immediate: true 20 } 21 } 22 } 23</script>

これで想定どおりの動作になるはずです。


追記

コメントで、Arrow関数式に対する誤解がみられるので少し書いておきます。
Arrow関数式は、定義された場所でのthisを__束縛します__。つまり、以下のコードは等価です:

javascript

1// これと 2let obj = { 3 test: (function(){...}).bind(this); 4}; 5// これはほぼ等しい 6let obj = { 7 test: () => {...}; 8};

定義された場所のthisというのがまた難しい概念なのですが、以下のコード例を使って説明します。

javascript

1var x = 5; 2let obj = { 3 x: 1, 4 func() {return this.x}, 5 func1: () => this.x 6}; 7obj.func() // ……1 8(0, obj.func)() // ……2 9 10obj.func1() // ……3 11(0, obj.func)() // ……4

このコードで、1と2の戻り値は異なります。
1の戻り値は、論ずるまでもなく1です。一方、2の戻り値は5となります。なぜかというと、1と2でthisの示すものが異なるからです。これは、特定のオブジェクトにobj.funcのthisが束縛されていないことに由来します。

他方、3と4の戻り値は共に5となります。なぜなら、定義された場所のthis.xは5だからです。定義された場所とは、自分自身を格納しているオブジェクトの中を__意味しません__。そのオブジェクトが定義された場所こそがthisの指し示す先になります。

そこで、新しいクラスの宣言方法では、インスタンスから直接呼ばない関数はArrow関数式で記述します。なぜなら、既存の無名関数式ではインスタンスから直接呼ばなければ、thisの示す先がインスタンスではなくなってしまうからです。一方Arrow関数式は想定された挙動をする、つまり直接呼ばなくてもthisはインスタンスを示します。

javascript

1class Test { 2 constructor(x) { 3 this.x = x; 4 } 5 func() { 6 return this.x; 7 } 8 func1 = () => this.x; 9} 10 11let ins = new Test(5); 12ins.func() // -> 5 13(0, ins.func)() // -> TypeError: Cannot read property 'x' of undefined 14ins.func1() // -> 5 15(0, ins.func1)() // -> 5

投稿2019/08/31 20:48

編集2019/09/04 03:24
frodo821

総合スコア322

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

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

tara-tail

2019/09/04 01:06

frodo821さん、返信が遅くなりまして申し訳ありません。 まず、質問させていただいた、callbackを使用しないやり方について、promiseでfindの結果を返すやり方を教えていただき、ありがとうございました。大変よく理解でき、コールバックを使用するやり方と両方のやり方を習得することができました。 また、thisについても詳細な説明をありがとうございます。自分はthisについては、結構その場しのぎでやっていた感もあり、もっと理解を深めなければと思っております。 大変恐縮なのですが、frodo821さんの説明の中で一つだけ教えてください。 (0, obj.func)() のような書き方は初めて見るもので、無名関数のようにも見えるのですが、この中の「0」は何を指しているのでしょうか?説明のあるURLでも結構ですので、教えていただけると助かります。
frodo821

2019/09/04 01:47

(0, func)は単に関数を別名で呼び出しているだけです。(0, eval)のようにしてグローバル名前空間で実行したり、(0, obj.func)のようにしてわざとthisの参照を変えたりするテクニックですね。 ここでの「,」はカンマ演算子といって、後者を返す演算子のひとつです。なので、0であることに必然性はありません。
Lhankor_Mhy

2019/09/04 03:05

横からすみません。 最後のクラス構文の (0, ins.func)() は this が undefined に束縛されるので、undefined.x が返せずエラーになるかと思います。
frodo821

2019/09/04 03:11 編集

いえ、そこのthisはグローバルオブジェクトに束縛されていますよ。グローバルオブジェクトは例えばnode.jsならglobalになりますし、ブラウザならwindowです。thisがundefinedになるような状況は、javascriptではstrictでなければ起こりません。この場合、'use strict'の宣言がないので、thisは普通にグローバルオブジェクトです。
frodo821

2019/09/04 03:20

あ、そうなんですね。babelでes5にしたときはstrictになってなかったので、そういう理解をしてました
Lhankor_Mhy

2019/09/04 03:23

おお、babelだとそうなんですね。知りませんでした。ありがとうございます。
frodo821

2019/09/04 03:29 編集

あ、今ちらっと確認したら、これ自分でデフォルトではstrictにしない設定にしてたみたいです。それがclass構文にも影響してただけっぽいですね。 なので回答の方修正しておきました。
Lhankor_Mhy

2019/09/04 03:30

それは、ちょっとした罠ですね……
frodo821

2019/09/04 03:33

ですね……。情報ありがとうございました!
tara-tail

2019/09/04 07:08

お忙しいところ、お二方の丁寧なご回答、本当にありがとうございました。少し自分には敷居の高い会話となってしまいましたが、大変奥の深い言語だと痛感しました。コツコツと勉強いたします。また、質問する機会があると思いますので、よろしくお願いいたします。
frodo821

2019/09/04 07:10

javascriptは奇妙な挙動の塊なので、意外と「え、そこで?」みたいな落とし穴があります。ちょっと触るだけなら簡単なんですけどね……。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問