可选回调的JavaScript样式


93

我有一些函数,有时(并非总是)会收到回调并运行它。检查回调是否已定义/函数是一种好的样式还是有更好的方法?

例:

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

在现代浏览器中,您可以使用它,typeof callback !== undefined所以省去了'
Snickbrack

1
如果您只是打电话save()?会因为缺少参数而发出错误或警告吗?还是完全可以,而回调只是简单的undefined
若奥·皮门特尔·费雷拉

Answers:


139

我个人更喜欢

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

typeof命令是狡猾的,但是仅应用于"undefined""function"

的问题typeof !== undefined在于,用户可能会传入定义的值而不是函数


3
typeof不是狡猾。有时含糊不清,但我不会称其为狡猾。但是,如果您打算将回调作为函数调用,则最好确认它实际上是一个函数,而不是一个数字。:-)
TJ Crowder

1
@TJCrowder狡猾可能是错误的单词,它的定义很明确,但由于装箱并有"object"95%的返回时间而没有用。
雷诺斯2011年

1
typeof不会造成拳击。typeof "foo""string",不是"object"。实际上,这是唯一可以分辨是要处理字符串原语还是String对象的唯一真实方法。(也许您在想Object.prototype.toString,这很方便,但是确实会造成拳击。)
TJ Crowder11年

4
&&顺便说一句,奇妙的习惯用法。
TJ Crowder

@TJCrowder您的意思是,我不知道比较字符串基元和装箱的String对象的值是什么。我也同意.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

2
同意 这也使测试更加容易,因为没有if条件。

5
但这不能解决类型问题,不是吗?如果我传递数组怎么办?或字符串?
Zerho '16

1
简化为callback = callback || function(){};
NorCalKnockOut

1
您还可以通过在声明中为其指定默认值来使参数成为可选参数:function save (callback=noop) { ...
Keith,

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它生成Array输入的所有可能组合

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

@Raynos-这是一个非常非常特定的用例。弱类型的JavaScript并不擅长区分类型,通常传递传入的参数比试图区分类型并猜测调用者想要什么要好:save( { callback : confirmProfileSaved, priority: "low" })
Malvolio

我不同意,有足够的类型检查功能。我更喜欢方法重载,但这是个人喜好。jQuery就是这种事情。
雷诺斯

@Raynos- 更糟糕的是忽略错误的参数,然后为执行非功能而抛出错误。考虑一个典型情况:库函数正在执行一些异步活动,并且需要通知调用函数。因此,一个不老练的用户,忽略和失败似乎是相同的:活动似乎永远不会完成。但是,对于经验丰富的用户(例如,原始程序员),失败会给出错误消息和直接指向问题的堆栈跟踪。忽略参数意味着繁琐的分析以确定什么导致“什么也没有发生”。
马尔沃里奥

尽管需要权衡。引发错误将使运行时崩溃,但忽略该错误将使周围的其他代码正常执行。
雷诺斯

1

有效的函数基于Function原型,请使用:

if (callback instanceof Function)

确保回调是一个函数


0

如果运行回调的标准是是否定义了回调,那么您就可以了。另外,我建议检查它是否真的还有功能。


0

我诚心地转移到coffee-script,发现默认参数是解决此问题的好方法

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.