Стиль JavaScript для додаткових зворотних викликів


93

У мене є деякі функції, які періодично (не завжди) отримуватимуть зворотний дзвінок і запускають його. Чи перевірка, чи визначено зворотний виклик / функцію, є хорошим стилем чи є кращий спосіб?

Приклад:

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

у сучасних браузерах ви могли б просто використовувати, typeof callback !== undefinedтак що залиште осторонь'
Snickbrack

1
а якщо ви просто зателефонуєте save()? Це не дасть попередження про помилку або посилання, оскільки аргумент відсутній? Або це цілком нормально, а зворотний дзвінок - це просто undefined?
Жоао Піментель Феррейра

Відповіді:


139

Я особисто віддаю перевагу

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

Однак typeofкоманда хитра, і її слід використовувати лише для "undefined"та"function"

Проблеми з тим typeof !== undefined, що користувач може передати значення, яке визначене, а не функцію


3
typeofне хитрий. Часом це розпливчасто, але я б не назвав це хитрим. Однак погодились, що якщо ви збираєтеся викликати зворотний виклик як функцію, найкраще перевірити, чи це насправді функція, а не, знаєте, номер. :-)
TJ Crowder

1
@TJCrowder хитрий може бути неправильним словом, він чітко визначений, але марний через бокс і повернення в "object"95% випадків.
Райнос

1
typeofне викликає боксу. typeof "foo"є "string", ні "object". Це насправді єдиний реальний спосіб, який ви можете сказати, чи маєте ви справу зі струнним примітивом чи Stringоб'єктом. (Можливо, ви думаєте про це Object.prototype.toString, що дуже зручно , але справді спричиняє бокс.)
TJ Crowder

4
Чудово ідіоматичне використання &&до речі.
TJ Crowder

@TJCrowder У вас є точка, я не знаю, яке значення має порівняння рядкових примітивів та об'єктів String в коробці. Крім того, я погоджуюся, що .toStringце чудово для пошуку [[Class]].
Raynos

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

1
Спростіть доcallback = callback || function(){};
NorCalKnockOut

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

@Raynos - Гірше ігнорувати неправильний параметр, а потім видавати помилку для виконання не функціональної функції. Розглянемо типовий випадок: функція бібліотеки виконує певну асинхронну діяльність, і про функцію виклику потрібно повідомити. Таким чином, непрофілізований користувач, ігнорування та збій здаються однаковими: діяльність, здається, ніколи не завершується. Однак досвідченому користувачеві (наприклад, оригінальному програмісту) відмова видає повідомлення про помилку та стек стеження, які вказують безпосередньо на проблему; ігнорувати параметр означає нудний аналіз, щоб визначити, що спричинило "нічого".
Malvolio

все ж є компроміс. Введення помилки призведе до краху часу виконання, але ігнорування нею дозволить іншим кодам виконуватись як звичайно.
Raynos

1

Дійсна функція базується на прототипі функції, використовуйте:

if (callback instanceof Function)

щоб переконатися, що зворотний дзвінок є функцією


0

Якщо критерієм запуску зворотного виклику є той, визначений він чи ні, то з вами все гаразд. Крім того, я пропоную перевірити, чи дійсно це функція додатково.


0

Я перейшов до сценарію кави і знайшов аргументи за замовчуванням - хороший спосіб вирішити цю проблему

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

0

Це можна легко зробити за допомогою ArgueJS :

function save (){
  arguments = __({callback: [Function]})
.....do stuff......
  if(arguments.callback){
    callback();
  };
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.