Hiểu rõ hơn về các hàm gọi lại trong JavaScript


163

Tôi hiểu việc chuyển một chức năng sang một chức năng khác như một cuộc gọi lại và thực hiện nó, nhưng tôi không hiểu cách thực hiện tốt nhất để làm điều đó. Tôi đang tìm một ví dụ rất cơ bản, như thế này:

var myCallBackExample = {
    myFirstFunction : function( param1, param2, callback ) {
        // Do something with param1 and param2.
        if ( arguments.length == 3 ) {
            // Execute callback function.
            // What is the "best" way to do this?
        }
    },
    mySecondFunction : function() {
        myFirstFunction( false, true, function() {
            // When this anonymous function is called, execute it.
        });
    }
};

Trong myFirstFunction, nếu tôi trả về hàm gọi lại mới (), thì nó hoạt động và thực thi hàm ẩn danh, nhưng đó dường như không phải là cách tiếp cận chính xác với tôi.


Đúng theo nghĩa nào? Thông thường các cuộc gọi lại được sử dụng cho các trình xử lý sự kiện - đáng chú ý nhất là các cuộc gọi Ajax, không đồng bộ - về cơ bản là những điều mà bạn không biết khi nào (hoặc nếu) một cuộc gọi sẽ đến.
cletus

2
bằng cách này, các đối số là mảng như nhưng không phải là mảng, vì vậy bạn không thể thực hiện đối số.length nhưng bạn có thể chuyển đổi nó thành một mảng bằng phương thức lát ...
paul

1
@paul, mặc dù bạn đúng argumentskhông phải là một mảng, bạn vẫn có thể tham chiếu độ dài của nó như là arguments.length- hãy thử. Thuộc tính này đề cập đến số lượng đối số thực sự được truyền vào và không nhất thiết là số lượng tham số trong chữ ký hàm.
hotshot309

Câu trả lời:


132

Bạn chỉ có thể nói

callback();

Thay phiên, bạn có thể sử dụng callphương pháp nếu bạn muốn điều chỉnh giá trị thistrong vòng gọi lại.

callback.call( newValueForThis);

Bên trong chức năng thissẽ là bất cứ điều gì newValueForThis.


91

Bạn nên kiểm tra xem có gọi lại hay không và là một hàm thực thi:

if (callback && typeof(callback) === "function") {
    // execute the callback, passing parameters as necessary
    callback();
}

Rất nhiều thư viện (jQuery, dojo, v.v.) sử dụng một mẫu tương tự cho các hàm không đồng bộ của chúng, cũng như node.js cho tất cả các hàm async (nodejs thường truyền errordatagọi lại). Nhìn vào mã nguồn của họ sẽ giúp!


Tại sao bạn đúc callbackthành chuỗi và sau đó kiểm tra loại của nó? Điều này sẽ tăng cường hiệu suất? Điều này giống như kiểm tra kiểu, kiểm tra xem boolean đã chuyển đổi có trả về true không và sau đó kiểm tra lại kiểu của nó và kiểm tra nó theo chuỗi ... Bạn có thể giải thích tại sao không?
đau đầu Bộ mã hóa

Tôi tò mò tại sao bạn cần xác nhận đầu tiên cho cuộc gọi lại ... là để kiểm tra null hoặc không xác định? Sẽ không typeof(callback)đạt được điều đó cho bạn? typeof(null) === "Object",typeof("undefined") === "undefined"
PJH

1
Ngắn mạch VÀ. Nếu cuộc gọi lại không tồn tại, đừng bận tâm đến việc tính toán kiểu của nó. Mặc dù, bạn đã đúng. Nó không cần thiết với typeof (), nhưng tôi sẽ thực hiện jsperf và xem liệu ngắn mạch có đáng không.
arunjitsingh

@headacheCoder - callbackkhông được truyền vào một chuỗi, loại của nó đang được kiểm tra để xem nó có phải là hàm không, trước khi nó được gọi. Mã có thể chấp nhận callbacknhư một đối số và không chắc chắn rằng đối số đó là loại có thể gọi được - hoặc có lẽ các đối số thuộc nhiều loại khác nhau trong nỗ lực cung cấp một dạng đa hình trong đó mã có thể phản ứng khác nhau với các typeofđối số khác nhau .
LeeGee

34

Có 3 khả năng chính để thực thi một chức năng:

var callback = function(x, y) {
    // "this" may be different depending how you call the function
    alert(this);
};
  1. gọi lại (đối số_1, đối số_2);
  2. callback.call (some_object, argument_1, argument_2);
  3. gọi lại.apply (some_object, [argument_1, argument_2]);

Phương pháp bạn chọn phụ thuộc vào việc:

  1. Bạn có các đối số được lưu trữ trong một mảng hoặc dưới dạng các biến khác biệt.
  2. Bạn muốn gọi hàm đó trong ngữ cảnh của một số đối tượng. Trong trường hợp này, sử dụng từ khóa "này" trong cuộc gọi lại đó sẽ tham chiếu đối tượng được truyền dưới dạng đối số trong lệnh gọi () hoặc áp dụng (). Nếu bạn không muốn vượt qua bối cảnh đối tượng, hãy sử dụng null hoặc không xác định. Trong trường hợp sau, đối tượng toàn cầu sẽ được sử dụng cho "này".

Tài liệu cho Function.call , Function.apply


6

Gọi lại là về tín hiệu và "mới" là về việc tạo các thể hiện đối tượng.

Trong trường hợp này, nó thậm chí còn phù hợp hơn khi thực hiện chỉ "gọi lại ();" hơn là "trả lại cuộc gọi lại mới ()" vì dù sao bạn cũng không làm gì với giá trị trả về.

(Và các đối số.length == 3 kiểm tra thực sự rất khó hiểu, fwiw, tốt hơn để kiểm tra xem paramback call tồn tại và là một hàm.)


6

việc thực hiện đúng sẽ là:

if( callback ) callback();

điều này làm cho tham số gọi lại tùy chọn ..


Điều gì xảy ra nếu đối số gọi lại không phải là một hàm?
Yaki Klein

2

Bạn có thể dùng:

if (callback && typeof(callback) === "function") {
    callback();
}

Ví dụ dưới đây là toàn diện hơn một chút:

function mySandwich(param1, param2, callback) {
  alert('Started eating my sandwich.\n\nIt has: ' + param1 + ', ' + param2);
  var sandwich = {
      toppings: [param1, param2]
    },
    madeCorrectly = (typeof(param1) === "string" && typeof(param2) === "string") ? true : false;
  if (callback && typeof(callback) === "function") {
    callback.apply(sandwich, [madeCorrectly]);
  }
}

mySandwich('ham', 'cheese', function(correct) {
  if (correct) {
    alert("Finished eating my " + this.toppings[0] + " and " + this.toppings[1] + " sandwich.");
  } else {
    alert("Gross!  Why would I eat a " + this.toppings[0] + " and " + this.toppings[1] + " sandwich?");
  }
});


1

Dưới đây là một ví dụ cơ bản giải thích callback()chức năng trong JavaScript:

var x = 0;

function testCallBack(param1, param2, callback) {
  alert('param1= ' + param1 + ', param2= ' + param2 + ' X=' + x);
  if (callback && typeof(callback) === "function") {
    x += 1;
    alert("Calla Back x= " + x);
    x += 1;
    callback();
  }
}

testCallBack('ham', 'cheese', function() {
  alert("Function X= " + x);
});

Câu đố


1

function checkCallback(cb) {
  if (cb || cb != '') {
    if (typeof window[cb] === 'undefined') alert('Callback function not found.');
    else window[cb].call(this, Arg1, Arg2);
  }
}

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.