関数を返す関数


109

私はこの「関数を返す関数」という概念にこだわっています。私は、Stoyan Stefanovによる「オブジェクト指向のJavascript」という本を参照しています。

スニペット1:

    function a() {
      
        alert('A!');
    
        function b(){
            alert('B!'); 
        }
    
        return b();
    }
    
    var s = a();
    alert('break');
    s();

出力:

A!
B!
break

スニペット2

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b;
}

var s = a();
alert('break');
s();
出力:

A!
break
B!

誰かが戻るbb()上のスニペットの違いを教えてもらえますか?


2
最初のスニペットがs();でエラーを出すことに気づくでしょう。
weirdalsuperfan 2018

回答:


121

(括弧なしの)関数に変数を割り当てると、関数への参照がコピーされます。括弧を関数名の最後に置くと、関数が呼び出され、関数の戻り値が返されます。

デモ

function a() {
    alert('A');
}
//alerts 'A', returns undefined

function b() {
    alert('B');
    return a;
}
//alerts 'B', returns function a

function c() {
    alert('C');
    return a();
}
//alerts 'C', alerts 'A', returns undefined

alert("Function 'a' returns " + a());
alert("Function 'b' returns " + b());
alert("Function 'c' returns " + c());

あなたの例では、関数内で関数も定義しています。といった:

function d() {
    function e() {
        alert('E');
    }
    return e;
}
d()();
//alerts 'E'

関数はまだ呼び出し可能です。それはまだ存在しています。これは常にJavaScriptで使用されます。関数は、他の値と同じように渡すことができます。以下を検討してください。

function counter() {
    var count = 0;
    return function() {
        alert(count++);
    }
}
var count = counter();
count();
count();
count();

関数countは、その外部で定義された変数を保持できます。これはクロージャーと呼ばれます。JavaScriptでもよく使用されます。


スニペット1:var hero = {name: 'Rafaelo'、sayName:function(){return hero.name; }、nayName:hero.sayName} hero.nayName(); スニペット2:var hero = {name: 'Rafaelo'、sayName:function(){return hero.name; }、nayName:this.sayName} hero.nayName(); 最初のスニペットでは正しい出力が得られますが、2番目のスニペットではそうではありません。どうして?よろしく。
Cafecorridor

this関数本体の内部のみを意味し、それ以外の場合はグローバルです。あなたが言っているのthis.sayNameは、sayName存在しないグローバル変数が欲しいということです、それは未定義なので、呼び出すことができません。
kzh

7
私はd()();に戸惑いました。最初は、最初の()がdを呼び出し、2番目の()がdの戻り値(e)を呼び出すことに気づきました。
skud

1
8年後、これはまだ関連しています!
Brad Vanderbush、

45

なしで関数名を()返すと、関数への参照が返されますvar s = a()。これは、で行ったように割り当てることができます。s関数への参照が含まれるようになりb()、呼び出しs()は機能的に呼び出しと同等になりb()ます。

// Return a reference to the function b().
// In your example, the reference is assigned to var s
return b;

()returnステートメントで関数を呼び出すと、関数が実行され、関数によって返された値が返されます。これは呼び出しvar x = b();に似ていb()ますが、戻り値を割り当てる代わりに、呼び出し元の関数からそれを返しますa()。関数b()自体が値を返さない場合、呼び出しはundefined、によって行われた他の作業の後に戻りますb()

// Execute function b() and return its value
return b();
// If b() has no return value, this is equivalent to calling b(), followed by
// return undefined;

1
すべての回答の中で、私はその単純さのためにあなたの回答をより気に入っています。
anar khalilov 2013年

よく置きます!この答えは非常にわかりやすい。
AndrewSteinheiser 2018年

関数が値を返さない場合、呼び出しが未定義を返すことについてビットを検証してくれてありがとう。私は最近この混乱を発見しました。関数が明示的にnullを返したとしても、変数に返された値を割り当てた場合、それはnullではなく未定義になります。nullとundefinedが===と完全に同等ではないため、一部のコードベースでこれが多くの奇妙な問題を引き起こしたと思います。
ベンジャミン

37

return b(); 関数b()を呼び出し、その結果を返します。

return b; 関数bへの参照を返します。これを変数に格納して、後で呼び出すことができます。


17

戻る bとは、関数オブジェクトを返すことです。JavaScriptでは、関数は他のオブジェクトと同様に単なるオブジェクトです。それが役に立たない場合は、「オブジェクト」という単語を「もの」に置き換えてください。関数から任意のオブジェクトを返すことができます。true / falseの値を返すことができます。整数(1,2,3,4 ...)。文字列を返すことができます。複数のプロパティを持つ複雑なオブジェクトを返すことができます。そして、関数を返すことができます。関数は単なるものです。

あなたの場合、返すbことはThingを返します、Thingは呼び出し可能な関数です。戻るとb()、呼び出し可能な関数から返された値が返されます。

このコードを考えてみましょう:

function b() {
   return 42;
}

上記の定義を使用してreturn b();、値42をreturn b;返します。一方、それ自体が42の値を返す関数を返します。これらは2つの異なるものです。


4
42;)を返す必要があります;
c69

4

を返すとb、関数bへの参照にすぎませんが、現時点では実行されていません。

を返すb()と、関数を実行してその値を返します。

試してみてくださいalertINGのtypeof(s)あなたの例で。スニペットbは「機能」を提供します。スニペットで何が得られますか?


最初のものは「未定義」を与えます。return b()は完全に役に立たないということですか?また、2番目のスニペットでは、関数bはプライベートです。それでは、関数の外の参照にどのようにアクセスできますか?可能であれば、この概念を明確に説明するリンクを提供してください。ありがとう!
Cafecorridor

最初の質問に対する答えを得ました。関数b()で1 + 2を返し、typeofは数値を示します。ありがとう。
Cafecorridor

わかりました。プライベート関数について:2つ目の例では、既に返されているため、実際にはプライベートではありません。実際には、に割り当てられsます。return this代わりに試してみてくださいreturn bs.b()その後は可能になります;)
vzwick

必ずやってみます。JavaScriptではまだこの概念に達していません。たぶん数日で。ありがとう!:)
Cafecorridor

function a(){alert( "A!"); 関数b(){alert( "B!"); } return b; } var s = a(); 削除a; s(); ---- end ---- Javascriptでの参照の概念はJavaと同じですか?ここでは、関数a()を削除しましたが、s()を呼び出すとb()が実行されます。だから私はsがbのコピーを含み、a()で定義されたb()を指していないと言えるでしょうか?
Cafecorridor

2

関数をintのような型として想像してみてください。関数でintを返すことができます。関数を返すこともできます。これらは「関数」タイプのオブジェクトです。

ここで構文の問題です。関数は値を返すため、値を返すのではなく、関数を返すにはどうすればよいでしょうか。

括弧を省略して!括弧がないと、関数は実行されません。そう:

return b;

「関数」を返します(数値を返す場合のように想像してください)。

return b();

最初に関数を実行し、次にそれを実行して得られた値を返します。これは大きな違いです!


2番目のスニペットでは、関数bはプライベートです。それでは、関数の外の参照にどのようにアクセスできますか?これを管理するルールはありますか?
Cafecorridor

JavaScript内のオブジェクト(これには関数が含まれます)は、共有するとプライベートではなくなります。
kzh

@Cafecorridor:プライベート関数が何か(パブリック関数)によって返されたり、パブリック変数(アプリケーションからアクセス可能な変数)に割り当てられたりする場合は、yourvariable();を簡単に実行できます、そうでない場合は、返された関数を変数に割り当てて、yourvariable();を
Francesco Belladonna、2011年

2

変数を作成します

var thing1 = undefined;

関数を宣言する:

function something1 () {
    return "Hi there, I'm number 1!";
}

(最初の変数)のを警告thing1

alert(thing1); // Outputs: "undefined".

関数への参照thing1になりたい場合、つまり、作成した関数と同じものにする場合は、次のようにします。something1

thing1 = something1;

ただし、関数のreturn が必要な場合は、実行した関数の戻り値を割り当てる必要があります。括弧を使用して関数を実行します。

thing1 = something1(); // Value of thing1: "Hi there, I'm number 1!" 

-1

スニペット1:

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b(); //return nothing here as b not defined a return value
}

var s = a(); //s got nothing assigned as b() and thus a() return nothing.
alert('break');
s(); // s equals nothing so nothing will be executed, JavaScript interpreter will complain

ステートメント「b()」は、テキスト「B!」を含むダイアログボックスを表示する「b」という名前の関数を実行することを意味します

ステートメント 'return b();' 「b」という名前の関数を実行し、「b」関数が返すものを返すことを意味します。しかし、 'b'は何も返しません。このステートメント 'return b()'も何も返しません。b()が数値を返す場合、「return b()」も数値です。

これで 's'には 'a()'が返すものの値が割​​り当てられ、これは 'b()'を返しますが、これは何もないので、 's'は何もありません(JavaScriptでは実際には「未定義」です。したがって、 JavaScriptに「s」のデータ型を解釈するように要求すると、JavaScriptインタープリターは「s」が未定義であることを通知します。)「s」は未定義であるため、JavaScriptにこのステートメント「s()」を実行するように要求すると、 JavaScriptに「s」という名前の関数を実行するように要求していますが、ここの「s」は「未定義」であり、関数ではないため、JavaScriptは「ねえ、sは関数ではないので、方法がわかりません。このsを使用して」、「キャッチされていないTypeError:sは関数ではありません」というエラーメッセージがJavaScriptによって表示されます(FirefoxとChromeでテスト済み)


スニペット2

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b; //return pointer to function b here
}

var s = a();  //s get the value of pointer to b
alert('break');
s(); // b() function is executed

現在、関数 'a'は 'b'という名前の関数へのポインタ/エイリアスを返します。したがって、「s = a()」を実行すると、「s」はbを指す値を取得します。つまり、「s」は「b」のエイリアスになり、「s」を呼び出すと「b」を呼び出すことになります。つまり、 's'は現在関数です。「s()」を実行すると、「b!表示されます(つまり、関数 'b'で 'alert(' B! ');ステートメントを実行します)


-2

これは実生活で非常に便利です。

Express.jsの操作

したがって、通常のexpressルートは次のようになります。

function itWorksHandler( req, res, next ) {
  res.send("It works!");
}

router.get("/check/works", itWorksHandler );

しかし、ラッパー、エラーハンドラー、またはsmthを追加する必要がある場合はどうでしょうか。

次に、ラッパーから関数を呼び出します。

function loggingWrapper( req, res, next, yourFunction ) {
  try {
    yourFunction( req, res );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", function( req, res, next ) {
  loggingWrapper( req, res, next, itWorksHandler );
});

複雑に見えますか?さて、これはどうですか:

function function loggingWrapper( yourFunction ) => ( req, res, next ) {
  try {
    yourFunction( req, res, next );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", loggingWrapper( itWorksHandler ) );

最後に、loggingWrapperある引数を持つ関数を別の関数として渡していて、引数として受け取る新しい関数を返していることitWorksHandlerを確認してください。loggingWrapperreq, res, next

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.