Kiểu JavaScript cho các lệnh gọi lại tùy chọn


93

Tôi có một số chức năng thỉnh thoảng (không phải lúc nào) sẽ nhận được một cuộc gọi lại và chạy nó. Việc kiểm tra xem lệnh gọi lại có được xác định / hoạt động hay không hay có cách nào tốt hơn không?

Thí dụ:

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

trong các trình duyệt hiện đại, bạn chỉ có thể sử dụng typeof callback !== undefinedđể bỏ qua'
Snickbrack

1
và nếu bạn chỉ đơn giản gọi save()? Điều đó sẽ không đưa ra một cảnh báo lỗi hoặc linting bởi vì một đối số bị thiếu? Hoặc nó hoàn toàn ổn và gọi lại đơn giản undefined?
João Pimentel Ferreira

Câu trả lời:


139

Cá nhân tôi thích

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

Các typeoflệnh là tuy nhiên tinh ranh và chỉ nên được sử dụng cho "undefined""function"

Các vấn đề với hàm typeof !== undefinedlà người dùng có thể chuyển vào một giá trị được xác định và không phải là một hàm


3
typeofkhông tinh ranh. Đôi khi nó mơ hồ, nhưng tôi sẽ không gọi nó là tinh ranh. Tuy nhiên, đồng ý rằng nếu bạn định gọi callback dưới dạng một hàm, tốt nhất bạn nên kiểm tra xem nó có thực sự là một hàm hay không, bạn biết đấy, là một số. :-)
TJ Crowder

1
@TJCrowder dodgy có thể là từ sai, nó được định nghĩa rõ ràng nhưng vô dụng do đấm bốc và quay lại "object"95% thời gian.
Raynos

1
typeofkhông gây ra quyền anh. typeof "foo""string", không phải "object". Đó thực sự là cách thực sự duy nhất bạn có thể biết liệu bạn đang xử lý một chuỗi nguyên thủy hay một Stringđối tượng. (Có lẽ bạn đang nghĩ đến Object.prototype.toString, đó là rất tiện dụng , nhưng không gây ra boxing.)
TJ Crowder

4
Nhân tiện sử dụng thành ngữ tuyệt vời &&.
TJ Crowder

@TJCrowder Bạn có một điểm, tôi không biết giá trị của việc so sánh chuỗi nguyên thủy và đối tượng chuỗi đóng hộp là gì. Ngoài ra, tôi đồng ý .toStringlà đáng yêu cho việc tìm kiếm [[Class]].
Raynos

50

Bạn cũng có thể làm:

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

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

Nó đặc biệt hữu ích nếu bạn tình cờ sử dụng callbackở một vài nơi.

Ngoài ra, nếu bạn đang sử dụng jQuery, bạn đã có một chức năng như vậy, nó được gọi là $ .noop


4
Theo quan điểm của tôi, đây là giải pháp thanh lịch nhất.
Nicolas Le Thierry d'Ennequin

2
Đã đồng ý. Điều này cũng làm cho việc kiểm tra dễ dàng hơn, vì không có điều kiện nếu.

5
nhưng điều này không giải quyết được vấn đề loại phải không? điều gì sẽ xảy ra nếu tôi truyền một mảng? hay một chuỗi?
Zerho

1
Đơn giản hóa thànhcallback = callback || function(){};
NorCalKnockOut

1
Bạn cũng có thể làm cho một tùy chọn lập luận bằng cách cho nó một giá trị mặc định trong tờ khai:function save (callback=noop) { ...
Keith

39

Đơn giản là làm

if (callback) callback();

Tôi muốn gọi lại cuộc gọi lại nếu được cung cấp, bất kể đó là loại nào. Đừng để nó âm thầm thất bại, để người triển khai biết mình đã chuyển đối số sai và có thể sửa nó.


11

ECMAScript 6

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

3

Thay vì đặt lệnh gọi lại là tùy chọn, chỉ cần gán mặc định và gọi nó bất kể điều gì

const identity = x =>
  x

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

Khi đã sử dụng

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

Một phong cách như vậy được gọi là phong cách tiếp diễn . Đây là một ví dụ thực tế, combinationstạo ra tất cả các kết hợp có thể có của đầu vào Mảng

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' ]
// ]

Bởi vì combinationsđược định nghĩa theo kiểu truyền tiếp tục, lệnh gọi trên có hiệu quả giống nhau

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

Chúng tôi cũng có thể chuyển một phần tiếp theo tùy chỉnh để thực hiện điều gì đó khác với kết quả

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

Phong cách truyền tiếp tục có thể được sử dụng với kết quả trang nhã đáng ngạc nhiên

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

Tôi đã quá mệt mỏi khi xem đi xem lại cùng một đoạn mã, tôi đã viết thế này:

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

Tôi có hàng trăm chức năng làm những việc như

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

hay bất cứ cái gì...

Tôi hoài nghi về toàn bộ chiến lược "đảm bảo rằng nó hoạt động". Các giá trị hợp pháp duy nhất là một hàm hoặc sai lệch. Nếu ai đó chuyển đến một số khác 0 hoặc một chuỗi không rỗng, bạn sẽ làm gì? Làm thế nào để bỏ qua vấn đề giải quyết nó?


Hàm có thể được nạp chồng để chấp nhận một số giá trị làm đối số đầu tiên, hàm làm đối số đầu tiên hoặc cả hai tham số. Có một trường hợp sử dụng để kiểm tra xem nó có phải là một chức năng hay không. Nó cũng tốt hơn để bỏ qua những thông số sai sau đó ném một lỗi để thực hiện một phi chức năng
Raynos

@Raynos - Đó là một trường hợp sử dụng rất, rất cụ thể. JavaScript, bị yếu gõ, là không giỏi phân biệt chủng loại, và nó thường là tốt hơn để vượt qua trong các thông số được đặt tên hơn là cố gắng để phân biệt các loại và đoán những gì người gọi muốn:save( { callback : confirmProfileSaved, priority: "low" })
Malvolio

Tôi không đồng ý, có đủ khả năng kiểm tra loại. Tôi thích quá tải phương thức hơn, nhưng đó là sở thích cá nhân. Đây là loại điều mà jQuery làm rất nhiều.
Raynos

@Raynos - Còn tệ hơn nếu bỏ qua tham số sai sau đó tạo ra lỗi khi thực thi một hàm không phải là hàm. Hãy xem xét một trường hợp điển hình: hàm thư viện đang thực hiện một số hoạt động không đồng bộ và hàm gọi cần được thông báo. Vì vậy, một người dùng không tinh vi, bỏ qua và thất bại dường như giống nhau: hoạt động dường như không bao giờ hoàn thành. Tuy nhiên, đối với một người dùng sành sỏi (ví dụ, lập trình viên ban đầu), lỗi sẽ đưa ra thông báo lỗi và dấu vết ngăn xếp chỉ trực tiếp vào vấn đề; bỏ qua tham số có nghĩa là phân tích tẻ nhạt để xác định điều gì đã khiến "không có gì" xảy ra.
Malvolio

Tuy nhiên, cần phải đánh đổi. Việc ném một lỗi sẽ làm hỏng thời gian chạy, nhưng việc bỏ qua nó sẽ cho phép các mã khác xung quanh nó thực thi như bình thường.
Raynos

1

Một hàm hợp lệ dựa trên nguyên mẫu Hàm, hãy sử dụng:

if (callback instanceof Function)

để đảm bảo gọi lại là một hàm


0

Nếu tiêu chí để chạy lệnh gọi lại là nó có được xác định hay không, thì bạn vẫn ổn. Ngoài ra, tôi khuyên bạn nên kiểm tra xem nó có thực sự là một chức năng bổ sung hay không.


0

Tôi đã chuyển sang coffee-script và thấy các đối số mặc định là một cách hay để giải quyết vấn đề này

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

0

Nó có thể được thực hiện một cách dễ dàng với ArgueJS :

function save (){
  arguments = __({callback: [Function]})
.....do stuff......
  if(arguments.callback){
    callback();
  };
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.