【问题标题】:JavaScript style for optional callbacks可选回调的 JavaScript 样式
【发布时间】:2011-10-11 04:23:09
【问题描述】:

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

例子:

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

【问题讨论】:

  • 在现代浏览器中,您可以只使用typeof callback !== undefined,所以省略'
  • 如果您只是致电save()?由于缺少参数,这不会给出错误或 linting 警告吗?或者完全没问题,回调只是undefined

标签: javascript coding-style callback


【解决方案1】:

ECMAScript 6

// @param callback Default value is a noop fn.
const save = (callback = () => {}) => {
   callback(); // Executes callback when specified otherwise does nothing
};

【讨论】:

    【解决方案2】:

    与其使回调可选,只需分配一个默认值并无论如何调用它

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

    使用时

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

    这样的样式称为continuation-passing style。这是一个真实的例子,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, ...)

    【讨论】:

      【解决方案3】:

      一个有效的函数基于函数原型,使用:

      if (callback instanceof Function)
      

      确保回调是一个函数

      【讨论】:

        【解决方案4】:

        简单地做

        if (callback) callback();
        

        如果提供了回调,我更喜欢调用回调,无论它是什么类型。不要让它静默失败,这样实现者就知道他传入了一个错误的参数并可以修复它。

        【讨论】:

          【解决方案5】:

          ArgueJS 可以轻松完成:

          function save (){
            arguments = __({callback: [Function]})
          .....do stuff......
            if(arguments.callback){
              callback();
            };
          };
          

          【讨论】:

            【解决方案6】:

            我已经开始使用 coffee-script,发现默认参数是解决这个问题的好方法

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

            【讨论】:

              【解决方案7】:

              我厌倦了一遍又一遍地看到同一个 sn-p 我写了这个:

                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" })
              • 我不同意,有足够的类型检查能力。我更喜欢方法重载,但这是个人喜好。这是 jQuery 经常做的事情。
              • @Raynos -- 更糟糕 忽略错误的参数然后抛出错误以执行非函数。考虑一个典型案例:库函数正在执行一些异步活动,需要通知调用函数。因此,一个不成熟的用户,忽略和失败似乎是一样的:活动似乎永远不会完成。但是,对于老练的用户(例如,最初的程序员),失败会给出错误消息和直接指向问题的堆栈跟踪;忽略参数意味着需要进行繁琐的分析以确定导致“什么都没有”发生的原因。
              • 尽管如此,还是需要权衡取舍。抛出错误会使运行时崩溃,但忽略它会让周围的其他代码正常执行。
              【解决方案8】:

              如果运行回调的标准是它是否已定义,那么你很好。另外,我建议检查它是否真的是一个额外的功能。

              【讨论】:

                【解决方案9】:

                你也可以这样做:

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

                如果您碰巧在一些地方使用了callback,它会特别有用。

                另外,如果你使用jQuery,你已经有一个类似的函数,它叫做$.noop

                【讨论】:

                • 但这并不能解决类型问题,不是吗?如果我传递一个数组怎么办?还是字符串?
                • 简化为callback = callback || function(){};
                • 您还可以通过在声明中给它一个默认值来使参数可选:function save (callback=noop) { ...
                • 如果用户提供的东西不是函数,那么您可以允许它崩溃,因为它违反了合同。无需处理所有可能的情况。
                【解决方案10】:

                我个人比较喜欢

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

                typeof 命令很狡猾,但只能用于"undefined""function"

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

                【讨论】:

                • typeof 并不狡猾。有时它很模糊,但我不会称之为狡猾。不过同意,如果您要将回调作为函数调用,最好检查它实际上是一个函数,而不是,你知道的,一个数字。 :-)
                • @T.J.Crowder dodgy 可能是错误的词,它的定义很好但由于拳击和返回"object" 95% 的时间而无用。
                • typeof 不会导致拳击。 typeof "foo""string",而不是 "object"。这实际上是您可以判断您是在处理字符串原语还是String 对象的唯一真实方法。 (也许您正在考虑Object.prototype.toString,即very handy,但确实会导致拳击。)
                • @T.J.Crowder 你说得有道理,我不知道比较字符串基元和盒装字符串对象的价值是什么。我也同意.toString 很适合找到[[Class]]
                • @T.J.Crowder 你提到它是你可以判断某物是原始还是对象的唯一方法。 This is a related question。也许你想发表评论。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多