オプションのコールバックのJavaScriptスタイル


93

コールバックを受信して​​実行することがある関数があります(常にではない)。コールバックが定義/機能されているかどうかをチェックすることは良いスタイルですか、それとももっと良い方法がありますか?

例:

function save (callback){
   .....do stuff......
   if(typeof callback !== 'undefined'){
     callback();
   };
};

最新のブラウザではtypeof callback !== undefinedそのまま使用できるので'
省略

1
そして単にあなたが電話した場合save()?引数が欠落しているため、エラーまたはリンティング警告が表示されませんか?またはそれは完全に大丈夫であり、コールバックは単にundefinedですか?
ジョアンピメンテルフェレイラ

回答:


139

個人的には

typeof callback === 'function' && callback();

このtypeofコマンドは危険ですが、次の場合にのみ使用し"undefined"てください"function"

の問題typeof !== undefinedは、ユーザーが関数はなく定義された値を渡す可能性があることです


3
typeof危険ではありません。あいまいなこともありますが、私はそれを危険だとは言いません。ただし、コールバックを関数として呼び出す場合は、それが実際には関数であり、数値ではないことを確認するのが最善であることに同意します。:-)
TJクラウダー'07

1
@TJCrowderの危険なことは間違った言葉かもしれません。それは明確に定義されていますが、ボクシングと"object"時間の95%を返すために役に立ちません。
レイノス

1
typeofボクシングを引き起こさない。typeof "foo""string"、ではありません"object"。これは実際に、文字列プリミティブとStringオブジェクトのどちらを扱っているかを判断できる唯一の実際の方法です。(たぶん、あなたはを考えているでしょう。Object.prototype.toStringこれは非常に便利ですが、ボクシングを引き起こします。)
TJ Crowder

4
&&ちなみに素晴らしく慣用的な使用。
TJクラウダー、

@TJCrowderポイントがあります。文字列プリミティブとボックス化された文字列オブジェクトを比較することの価値はわかりません。また、私.toStringはを見つけるのが素敵だと思い[[Class]]ます。
レイノス

50

次のこともできます:

var noop = function(){}; // do nothing.

function save (callback){
   callback = callback || noop;
   .....do stuff......
};

callbackいくつかの場所でを使用する場合に特に便利です。

さらに、を使用している場合jQuery、すでにそのような関数があり、$。noopと呼ばれます。


4
私の見解では、これは最もエレガントなソリューションです。
Nicolas Le Thierry d'Ennequin 2013

2
同意した。また、if条件がないため、テストが容易になります。

5
しかし、これはタイプの問題に対処していませんか?配列を渡すとどうなりますか?または文字列?
Zerho 2016年

1
簡略化callback = callback || function(){};
NorCalKnockOut 2017年

1
また、宣言の中でそれをデフォルト値を与えることによって、引数のオプションを行うことができますfunction save (callback=noop) { ...
キース

39

単に行う

if (callback) callback();

コールバックが提供されている場合は、それがどのタイプであっても呼び出すことを好みます。サイレントに失敗しないようにしてください。そうすれば、実装者は間違った引数を渡したことがわかり、修正できます。


11

ECMAScript 6

// @param callback Default value is a noop fn.
function save(callback = ()=>{}) {
   // do stuff...
   callback();
}

3

コールバックをオプションにするのではなく、デフォルトを割り当てて、それを呼び出します。

const identity = x =>
  x

const save (..., callback = identity) {
  // ...
  return callback (...)
}

使用時

save (...)              // callback has no effect
save (..., console.log) // console.log is used as callback

このようなスタイルは、継続渡しスタイルと呼ばれます。以下combinationsは、配列入力のすべての可能な組み合わせを生成する実際の例です。

const identity = x =>
  x

const None =
  Symbol ()

const combinations = ([ x = None, ...rest ], callback = identity) =>
  x === None
    ? callback ([[]])
    : combinations
        ( rest
        , combs =>
            callback (combs .concat (combs .map (c => [ x, ...c ])))
        )

console.log (combinations (['A', 'B', 'C']))
// [ []
// , [ 'C' ]
// , [ 'B' ]
// , [ 'B', 'C' ]
// , [ 'A' ]
// , [ 'A', 'C' ]
// , [ 'A', 'B' ]
// , [ 'A', 'B', 'C' ]
// ]

combinations継続渡しスタイルで定義されているため、上記の呼び出しは事実上同じです

combinations (['A', 'B', 'C'], console.log)
// [ []
// , [ 'C' ]
// , [ 'B' ]
// , [ 'B', 'C' ]
// , [ 'A' ]
// , [ 'A', 'C' ]
// , [ 'A', 'B' ]
// , [ 'A', 'B', 'C' ]
// ]

結果を他の何かにするカスタム継続を渡すこともできます

console.log (combinations (['A', 'B', 'C'], combs => combs.length))
// 8
// (8 total combinations)

継続渡しスタイルは驚くほどエレガントな結果で使用できます

const first = (x, y) =>
  x

const fibonacci = (n, callback = first) =>
  n === 0
    ? callback (0, 1)
    : fibonacci
        ( n - 1
        , (a, b) => callback (b, a + b)
        )
        
console.log (fibonacci (10)) // 55
// 55 is the 10th fibonacci number
// (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...)


1

同じスニペットを何度も目にするのにうんざりしているので、次のように書きました。

  var cb = function(g) {
    if (g) {
      var args = Array.prototype.slice.call(arguments); 
      args.shift(); 
      g.apply(null, args); 
    }
  };

私は次のようなことをする何百もの関数を持っています

  cb(callback, { error : null }, [0, 3, 5], true);

または何でも...

「機能を確認する」戦略全体には懐疑的です。唯一の正当な値は、関数または偽物です。誰かがゼロ以外の数値または空でない文字列を渡した場合、どうしますか?問題を無視してそれをどのように解決しますか?


関数はオーバーロードされて、最初の引数としていくつかの値を受け入れるか、最初の引数として関数を受け入れるか、または両方のパラメーターを受け入れる場合があります。それが関数であるかどうかをチェックするユースケースがあります。また、誤ったパラメーターを無視してから、非関数を実行したときにエラーをスローすることをお
勧めし

@Raynos-これは非常に具体的な使用例です。JavaScriptは、弱く型付けされ、種類を区別するのが得意ではありません、それは種類を区別して、発信者が望んでいるものを推測しようとするよりも、名前付きパラメータに渡す方が良いでしょう:save( { callback : confirmProfileSaved, priority: "low" })
Malvolio

私は同意しません、型チェック能力は十分です。私はメソッドのオーバーロードを好みますが、それは個人的な好みです。これは、jQueryがよく行うことです。
レイノス

@Raynos- 間違ったパラメーターを無視してから、非関数を実行するためにエラーをスローするほうが悪いです。典型的なケースを考えてみましょう:ライブラリ関数が非同期アクティビティを実行していて、呼び出し元の関数に通知する必要があります。したがって、洗練されていないユーザーは、無視することと失敗することは同じように見えます。アクティビティは完了しないように見えます。ただし、熟練したユーザー(たとえば、元のプログラマー)にとっては、失敗はエラーメッセージと、問題を直接指すスタックトレースを提供します。パラメータを無視することは、「何も起こらなかった」原因を特定するための面倒な分析を意味します。
Malvolio、2011

ただし、トレードオフがあります。エラーをスローすると、ランタイムがクラッシュしますが、無視すると、その周りの他のコードが通常どおり実行されます。
レイノス

1

有効な関数は、Functionプロトタイプに基づいています。次を使用します。

if (callback instanceof Function)

コールバックが関数であることを確認する


0

コールバックを実行する基準が、定義されているかどうかに関係なく、それで問題ありません。また、それが本当に機能しているかどうかも確認することをお勧めします。


0

私はコーヒースクリプトに移動し、デフォルトの引数がこの問題を解決する良い方法であることを見つけました

doSomething = (arg1, arg2, callback = ()->)->
    callback()

0

それはeasillyで行うことができますArgueJS

function save (){
  arguments = __({callback: [Function]})
.....do stuff......
  if(arguments.callback){
    callback();
  };
};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.