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

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

ただいまの
回答率

87.96%

JS : promise().then(console.log) が成功する理由がわからない

解決済

回答 3

投稿

  • 評価
  • クリップ 2
  • VIEW 2,348

score 86

掲題の通りです。

promise()
  .then((response) => console.log(response));

上記が成功するのはわかります。

promise()
  .then(console.log);

これだと、結果受け取っていないように思われるため、どうして成功するのかわかりません。

これがどのような省略記法なのか、教えていただきたいです。
よろしくお願いします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+7

promise().then(fn)は、Promiseインスタンスのthenメソッドの実行という意味です。
thenメソッドを実行する時は、引数に関数を指定して下さいねと言っています。
つまり、引数を要求する関数を渡せば何でもいいんですよ。


まず、JSでは関数を第一級オブジェクトとして取り扱っている為、以下の特性があります。

  • 変数に代入できる
  • 関数の引数として設定できる
  • 関数の戻り値として設定できる

console.logはConsoleというインスタンスで、プロトタイプメソッドとしてlogがぶら下がっています。
このメソッドはthisがconsoleインスタンスに束縛されているだけの関数です。

関数やメソッドはお尻に()をつける事で実行されます。
console.logのようにカッコを付けずにアクセスすることで、
関数の実体にアクセスすることが可能です。

関数としてアクセスしたメソッドは、代入演算子で変数に格納したり、
別の関数やメソッドの引数として利用することが可能です。

例としてArray.prototype.forEachにconsole.logを千切って渡してみましょう。

[1, 2, 3].forEach(console.log);
// 1 0 (3) [1, 2, 3]
// 2 1 (3) [1, 2, 3]
// 3 2 (3) [1, 2, 3]

forEachはコールバック関数に対して「要素の値、インデックス値、配列そのもの」を引数として渡すので、可変引数のconsole.logを渡すと出力結果が大騒ぎになりますが、普通に動作している事が分かります。


console.logは別にthisに強く依存する作りではないので、
メソッドを千切ってコールバック引数として指定するだけでも動作します。

ただし、インスタンスのメソッドは基本的にthis、つまりプロパティに依存するものが多いはず。
プロパティに依存しないなら最初から静的メソッドや普通の関数で作るケースが多いですからね。

ちょっと変数にメソッドを千切って保存することで、
thisの束縛が切れてしまうことを確認してみましょう。

class Cat {
  constructor (name) {
    this.name = name;
  }
  say () {
    return `${(this || {}).name}: meow`;
  }
}

const tama = new Cat('tama');
console.log(tama.say()); // tama: meow

const say = tama.say;
console.log(say()); // Undefined: meow

鳴き声sayのプロトタイプメソッドだけ保存しておこうと別の変数に保存すると、
見事に捨て猫になってしまいました。
無名関数に包むのが楽ですが、bindを使ったり、引数に束縛したりと色々と対応策はあります。

// 対応策色々
const say2 = tama.say.bind(tama);
const say3 = () => tama.say();
const say4 = (cat => () => cat.say())(tama);

console.log(say2()); // tama: meow
console.log(say3()); // tama: meow
console.log(say4()); // tama: meow

ここの部分は難解で質問文時点では蛇足気味ですが、
JSで高度な処理を組む時にシンプルに書けるようになってくるので必要に応じて勉強してみてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/26 21:19 編集

    なるほど。。
    わかった気にはなりました。
    これまで、ここのthisは何を指すだとか、bind()の意味などを深く考えずに書いていましたが、今一度、しっかりと学びたいと思います。

    2点だけ、自分の認識が間違っていないか確認したいので、お手すきの際にご回答いただければ幸いです。

    説明文中の
    ```
    const say = tama.say;
    ```

    ```
    const say = function say() {
    return `${(this || {}).name}: meow`;
    }
    }
    ```
    と同義であり、その関数単体ではthisもくそもないので、捨て猫になってしまうという認識で合っていますでしょうか?

    `tama.say`はCatクラス内のsay()関数の実体であるという認識です。

    また、この`say`そのものを使い、どうにかしてthisを設定して猫に名前を付けることは、不可能ということであっていますでしょうか?

    よろしくお願いします。

    キャンセル

  • 2018/10/29 10:00

    そうですね。

    ただし、微妙に動きが違うケースがあり、
    試しに回答文のsayメソッドの中身を`return `${this.name}: meow`;`にするとthis=undefinedなのでundefinedには何のプロパティもありませんよエラーになるはずです。
    コメントにあったsay変数に直接関数を宣言した場合はthisはfunction関数によってthisが生成され、this.nameが空文字列を返すはずです。

    > この`say`そのものを使い、どうにかしてthisを設定して猫に名前を付けることは、不可能ということであっていますでしょうか?
    可能です。
    全ての関数はbindというthisや引数を束縛した関数を生成するメソッドを有しており、this.nameを使いたいならそれを何かしらで補ってやれば動作します。
    say.bind({name: 'tama'})() // "tama: meow"
    say.bind({name: 'kuro'})() // "kuro: meow"

    キャンセル

  • 2018/10/29 17:25

    ご解答ありがとうございました。

    > 全ての関数はbindというthisや引数を束縛した関数を生成するメソッドを有しており
    bindについてはもう少しきちんと学ぶ必要がありそうですが、何となく理解はできました。ありがとうございました。

    キャンセル

checkベストアンサー

+6

then()は、関数を受け取り、その関数にresponseをセットして呼び出すからだと思います。

〜.then(hoge)という呼び出しに対して、
Promiseの内部では、

  hoge(response);


が呼び出されるということです。

一つ目の例を、

  const func = (response) => {console.log(response));
  promise.then(func);


と置きかえると、

  func(response);  //console.log(response);の呼び出し


が呼び出されることになり、

結果としては、質問の二つのソースは同じ意味になりますね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/26 09:20

    なるほど。Promiseのthen内部では、明示しなくてもレスポンスが引数に代入されるため、console.logの引数にレスポンスが勝手に代入されるということですね。
    非常に明快なご説明、ありがとうございました。

    キャンセル

  • 2018/10/26 10:46

    明示しなくても、という感じより、then()の仕様として、「引数には、関数をセットしてね。resolveした時に引数に結果をセットして、その関数を呼び出すから。」というイメージだと思います。
    今回の例でいえば、セットした関数が、console.logか自作した関数か、という違いですね。

    キャンセル

  • 2018/10/26 12:55

    yambejpさんの回答のコメント欄にあるやりとりから考えると、オブジェクトに所属する関数を、直接コールバック引数の対象として渡す(今回の例でいうと、.then(console.log)のような呼び出し方)のは危ない(気づきにくい不具合を作り込んでしまう)ようなので最初の書き方(無名のアロー関数を定義してセット)を習慣づけるほうが、安全かもしれませんね。

    キャンセル

+1

コールバックしているだけですね

["a","b","c"].forEach(console.log)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/26 11:21 編集

    Node.jsのconsole.logはv0.12系で確認してもthis非依存でした。
    Chromeでもthis非依存で普通に使えます。
    ただし、[1, 2, 3].forEach(fn)で渡される引数の順番は「要素、インデックス、本体の配列そのもの」の3要素である為、console.logを入れて1, 2, 3を表示するのに使うとゴミデータがうじゃうじゃ出てきてdamm itになります。

    キャンセル

  • 2018/10/26 11:27

    なるほど・・・log()の中でthisを利用した実装があれば、影響しそうですね。ユーザーがやっちゃう確率が高い気もするので、明示的にconsoleオブジェクトを参照する?など、何か工夫してるんですかね?

    this・・・おまじない的に、bind()使ったり、アロー関数にしてしまったりして、あまり意識してませんが、これは意識しておかないと、怖い仕様ですね(^_^;)

    @maisumakun さん
    勉強になりました。ご教授ありがとうございました!

    キャンセル

  • 2018/10/26 12:02

    実際、高精度に時間を取るためのperformance.nowというメソッドがありますが、これはthisがperformance以外になると失敗しました。

    キャンセル

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

  • ただいまの回答率 87.96%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る