2

私は何年ものプログラミング経験がありますが、私の背景はJavascriptではありません。これに加えて、今日のJavascriptは私が何年も前にやったJavascriptではないという事実です。はるかに洗練された強力です。つまり、私はいくつかの関数のペイロードダイナミクスを理解するのに苦労しています。JavaScript関数の読み込み

関数が実際に何かを返す関数呼び出しは直感的ですが、Javascriptは頭がおかしい機能で何かをするようです。私はちょうどコードをコピー/ペーストすることができます、または私は自分のコードでこのパターンを再利用する方法を試してみることができます。

たとえば、次のMongoose呼び出しは、Userモデル内のすべてのレコードの検索を行い、呼び出しの結果は、渡された関数の2番目の引数(参照先)で終了します。

User.find({}, function(err, users) { // Get all the users 
    if (err) throw err; 

    console.log(users); // Show the JSON Object  
}); 

単純なforEachを配列に使用した別の例を次に示します。どうにかして、forEachは 'user'引数を設定します。

users.forEach(function(user) { 
     console.log(user.username, ': Admin = ', (user.admin ? 'Yes' : 'No')); 
}); 

誰でもこれを説明することができますか、どのように/なぜこの作品が良いのガイドを教えてください?

Node.js全体で同じパターンが見られましたが、それはちょっとした障害です。

私は何かが分かりませんでしたか、これは機能プログラミングに関する特殊なものですか?

ジョン

+3

研究 'javascriptコールバック'。 – charlietfl

+1

このテクニックのより一般的な理解が必要な場合は、[継続パッシングスタイル](https://en.wikipedia.org/wiki/Continuation-passing_style)を参照してください。 – naomik

+0

このスタイルでは本当に新しいことは何もありません。最も初期のjavascriptバージョンでは、イベントハンドラとしてコールバックが使用されていました。 – Bergi

答えて

1

Continuation Passing Styleと呼ばれます。これは、あなたが提供したMongooseの例のように非同期動作をカプセル化するために使用されることがありますが、それ以外の場合は.forEachのような同期的な方法で使用できます。

これがどのように機能するかを確認するには、独自のforEachを構成するのが簡単です。

function forEach(xs, f) 
    for (var i=0, len=xs.length; i<len; i++) { 
    f(x[i]); 
    } 
} 

forEach([1,2,3], function(x) { console.log(x); }) 

だから、これが機能する方法を見て、かなり簡単なはず:私たちはxsが、我々は関数の内部で定期的にforループを行う私たちの配列[1,2,3]に設定されていることがわかります。次に、fがループ内の要素ごとに1回呼び出されます。

実際のところ、JavaScriptの機能はfirst-class membersで、higher-order functionsを使用することができます。これは、関数が引数として受け入れられるため、.forEachは高次関数と見なされることを意味します。

このように、forEachは多くの異なる方法で実装できます。ここに別のものがあります。

forEach(xs, f) { 
    if (xs.length > 0) { 
    f(xs[0]); 
    forEach(xs.slice(1), f); 
    } 
} 

ここでのアイデアは、JavaScriptで関数を送信することに慣れていることです。別の関数を適用した結果として関数を返すこともできます。

function add(x) { 
    return function(y) { 
    return x + y; 
    } 
} 

function map(xs, f) { 
    function loop(ys, xs) { 
    if (xs.length === 0) 
     return ys; 
    else 
     return loop(ys.concat(f(xs[0])), xs.slice(1)); 
    } 
    return loop([], xs); 
} 

map([1,2,3], add(10)); //=> [11,12,13] 

長い前に、膝の深いfunctional paradigmおよびその他の新あらゆる種類のものを学ぶことができるでしょう。

機能!

+1

'forEach'はCPSとはあまり関係がありませんか? – Bergi

+0

したがって、コールバックに焦点を当てるのは混乱します。私の質問のforEachの例では、userは関数ではなく、オブジェクトであるが、それは簡単にNumberである可能性がある。 CPSは少し啓発されていますが、コールバックに焦点を当てても私を助けません。 –

+0

右私はそれを得る、私はコールバックの範囲内の値を持つユーザーと、値を割り当てられているユーザーを混乱させてきました。コールバックは隠されているので、完全に明確ではありませんでした。 –

1

あなたが基本的な考え方は、あなたが別の関数の引数としての機能を渡し、あなたがそれを必要なときにそれを呼び出すことですjavascript callbacks

必要です。

function basic(callback){ 
    console.log('do something here'); 

    var result = 'i am the result of `do something` to be past to the callback'; 

    // if callback exist execute it 
    callback && callback(result); 
} 

これはjavascriptの中核概念の1つです。でも、ajax httpリクエストのような非同期操作については、Promisesを見てみることをお勧めします。現在のES5仕様の一部ではありませんが、これには多くのライブラリとポリフィルムがあります。

function get(url) { 
    // Return a new promise. 
    return new Promise(function(resolve, reject) { 
    // Do the usual XHR stuff 
    var req = new XMLHttpRequest(); 
    req.open('GET', url); 

    req.onload = function() { 
     // This is called even on 404 etc 
     // so check the status 
     if (req.status == 200) { 
     // Resolve the promise with the response text 
     resolve(req.response); 
     } 
     else { 
     // Otherwise reject with the status text 
     // which will hopefully be a meaningful error 
     reject(Error(req.statusText)); 
     } 
    }; 

    // Handle network errors 
    req.onerror = function() { 
     reject(Error("Network Error")); 
    }; 

    // Make the request 
    req.send(); 
    }); 
} 

// Use it! 
get('story.json').then(function(response) { 
    console.log("Success!", response); 
}, function(error) { 
    console.error("Failed!", error); 
}); 
+0

でも、約束はコールバックを使用するので、最初はいつもコールバックの概念を理解する必要があります。 – Bergi

+0

ありがとう - 非常に便利 –

1

これらのコールバックは類似していますが、まったく異なる目的を果たします。最初の例では、User.findが非同期関数であるため、結果の取得にコールバックが使用されています。非同期の性質は、コールバック引数の順序のNodejs規約の背後にある理由です。コールバックの最初の引数は常にエラーのためのものです。

2番目の例では、コールバックを使用する主な理由はローカルスコープを作成することです。ループ内で非同期操作を実行する場合には非常に便利です。例えば:OtherModle.findが実行されるときvarで定義された変数は既に配列の最後の項目によって上書きされるので

users.forEach(function(user) { 
    Model.find({},function(er,rows){ 
     if(er){ 
      return handle(er); 
     } 
     OtherModel.find({userid: user.id},function(er,result){ 
      if(er){ 
       return handle(er); 
      } 
      console.log(result); 
     }); 
    }); 
}); 

は、上記の例では、Cスタイルのループでは動作しないmigth。

+0

他にも便利 - ありがとう –

2

Javascriptでは、関数もオブジェクトであり、変数に格納することができます。別の関数に渡される関数は、通常「コールバック」と呼ばれます(これは他の言語でも使用されますが、そこにはありません)。

polyfill、特にコールバックをトリガーする行を調べると便利です。

JavaScript関数もオブジェクトであるため、関数にはcallapplyがあり、関数をトリガーし、その関数の値をthisに設定することもできます。

コールバックの例は、(それがここにあります...私は知っている愚かだfiddle):

function callIf(val, callbackFn) { 
    // "arguments" is special in javascript, and it's not an array (although it does have an index selector). 
    // I can call Array's slice method passing "arguments" as the "this" of the function 
    var args = Array.prototype.slice.call(arguments, 2); 
    if(val) { 
     callbackFn.apply(this, args); 
    } 
} 

var values = [ 
    "Hop", 
    "on", 
    "Pop", 
    "Sam", 
    "I", 
    "Am" 
]; 


values.forEach(function(val) { 
    // note: referencing inner "log" function instead of "console.log" because "console.log" require's the "this" to be "console". 

    callIf(val.length < 3, log, val + " is a small word."); 

    function log(val) { 
     console.log(val); 
    } 
}); 

サイドノート:

あなたは、静的型言語の背景から来ている、とのようにJavascriptを発生している場合最初に動的に型付けされた言語ですが、私のアドバイスは:Javascriptがもたらす柔軟性を心配せずに受け入れますが、一貫性と優れたプログラミング規律を維持してください。シンプルさと可読性を強調する。楽しんでください:)

+0

ありがとう - 非常に有用 –

関連する問題