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

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

ただいまの
回答率

87.78%

javascriptのエラー原因がわからない

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 843

score 45

class sample{
    searchavailavle(){
      var arr=[]
      for(var i=0;i<8;i++){
      for(var j=0;j<8;j++){
        if(this.isavailable([i,j])){
          arr.push([i,j])
        }
      }
      }
      return arr
    }

    randomput(){
      var arr=this.searchavailavle();
      var randnum =  Math.floor( Math.random() * arr.length )
      console.log(arr);
      this.putstone(arr[randnum])}



    check(id){
      var y=parseInt(id/10)
      var x=id%10
      this.putstone([y,x])
      setTimeout(this.randomput(),1000);//←該当の問題箇所
    }

}
var x=new Board(8);
x.check(24)

上記のようなコード(一部抜粋)を書いたのですが、これを実行すると下記のようなエラーが発生します

timers.js:121
    throw new ERR_INVALID_CALLBACK(callback);
    ^

TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined

これはthis.randomput()が関数ではないよ、ということらしいのですがネットで情報を調べていたところかっこを外せばいいということらしいので(なぜカッコを外すと別の何かになるのかも知りたいですが)おそらく関数の戻り値をそのままsetTimeoutに入れてしまってるんだろうな、と想像しながら下記のようにコードを変えてみました。

setTimeout(this.randomput,1000);

すると今度は

      var arr=this.searchavailavle();
                   ^

TypeError: this.searchavailavle is not a function

のように出てきてしまいました。
関数の中で関数を呼び出しているだけなのになぜこのようなエラーがおこるのでしょうか?
ちなみに、setTimeoutで括らないと普通に即時実行されます。
2日くらいこの問題に向き合ってますが一向に解決できません。
お助けのほどよろしくお願い申し上げます。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • kei344

    2020/01/23 12:20

    「コードを変えてみました」と有りますが、コードが変わっていないようです。

    キャンセル

回答 2

+5

setTimeout(()=>this.randomput(),1000)ではどうでしょう?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/01/23 12:23

    いけました!!なぜでしょう。
    自分も関数にしないといけないのかなと思いまして下記のように変えてみたりもしましたが駄目でした。
    setTimeout(function(){this.randomput()},1000)

    キャンセル

  • 2020/01/23 12:35

    `function(){}`で包むと新しくthisが作られるので
    randomputメソッド内で「this.xxxなんて存在しないじゃないか!」と叱られます

    ES2015という新しい仕様で追加された`() => {}`というアロー関数は
    thisを生成しないのでメソッド内で使い回すにはとても使い勝手が良いものになります。

    this束縛が切れるあたりの話は私の回答も見て下さいね。

    キャンセル

+4

setTimeout(this.randomput(),1000);

変数等の後にカッコをつけるのは、
JavaScriptに於いては「関数・メソッドの実行」を意味します。

このrandomputメソッドを実行するタイミングはsetTimeoutによる1000ミリ秒後想定ですよね?
ならば今実行してはいけません。

setTimeout(this.randomput,1000);のようにカッコ無しに変更することで
setTimeoutの中の処理の方が引数の関数を1秒後に勝手に実行してくれるようになります。

this.searchavailavle is not a function

setTimeout(this.randomput,1000);に変更したことで
thisの束縛が剥がれた事が原因です。

JSの関数は引数に関数やメソッドを投げ込んで後から実行が出来る言語です。
しかし、メソッドをこのように引数として他人に受け渡してしまうとthisの束縛が切れてしまいます。

JavaScriptでは関数とメソッドの違いが明確には区切られて無く
同じFunctionオブジェクトを関する同じものです。
なのでthisを束縛するbindメソッドが用意されています。

デモの部分が英語で「うっ……」となりそうなので翻訳

// オブジェクトを宣言しておく
// 動作的にはクラス・インスタンスとほぼ同じになるので再現するだけならこれで十分
const module = {
  x: 42,
  getX: function() {
    return this.x;
  }
}

// 質問文は関数の引数として利用したが、
// 変数にメソッドを束縛してもthisが切り離されてしまう
const unboundGetX = module.getX;
console.log(unboundGetX());
// undefined

// thisはこれなんだよと明示してあげる
const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// 42 <- module.xの数値が正しく取り出せた

これと同じでこのようにすれば動きます。
setTimeout(this.randomput.bind(this), 1000);

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/01/23 12:51

    なるほど!関数の所有権の問題だったんですね。下のコメントの方も拝見させて頂きましたが、javascriptでは関数もオブジェクトとして扱っており、(確か元々class構文がなくfunctionで代替していたとみた事があります)新しいオブジェクトとして作られてしまったと言うことなんですね。アロー関数は完全にメソッドとしてなっておりオブジェクトを生成しない、と言う認識ですね。

    キャンセル

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

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

関連した質問

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