Các hàm trả về một hàm


109

Tôi bị mắc kẹt với khái niệm 'Các hàm trả về các hàm'. Tôi đang tham khảo cuốn sách 'Javascript hướng đối tượng' của Stoyan Stefanov.

Đoạn mã một:

    function a() {
      
        alert('A!');
    
        function b(){
            alert('B!'); 
        }
    
        return b();
    }
    
    var s = a();
    alert('break');
    s();

Đầu ra:

A!
B!
break

Đoạn trích hai

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b;
}

var s = a();
alert('break');
s();
Đầu ra:

A!
break
B!

Ai đó có thể vui lòng cho tôi biết sự khác biệt giữa trở lại bb()trong các đoạn trích trên không?


2
Bạn sẽ nhận thấy đoạn mã đầu tiên báo lỗi trên s ();
weirdalsuperfan

Câu trả lời:


121

Việc gán một biến cho một hàm (không có dấu ngoặc đơn) sẽ sao chép tham chiếu đến hàm. Đặt dấu ngoặc ở cuối tên hàm, gọi hàm, trả về giá trị trả về của hàm.

Bản giới thiệu

function a() {
    alert('A');
}
//alerts 'A', returns undefined

function b() {
    alert('B');
    return a;
}
//alerts 'B', returns function a

function c() {
    alert('C');
    return a();
}
//alerts 'C', alerts 'A', returns undefined

alert("Function 'a' returns " + a());
alert("Function 'b' returns " + b());
alert("Function 'c' returns " + c());

Trong ví dụ của bạn, bạn cũng đang xác định các hàm trong một hàm. Nhu la:

function d() {
    function e() {
        alert('E');
    }
    return e;
}
d()();
//alerts 'E'

Hàm vẫn có thể gọi được. Nó vẫn tồn tại. Điều này được sử dụng trong JavaScript mọi lúc. Chức năng có thể được thông qua xung quanh chỉ như các giá trị khác. Hãy xem xét những điều sau:

function counter() {
    var count = 0;
    return function() {
        alert(count++);
    }
}
var count = counter();
count();
count();
count();

Hàm đếm có thể giữ các biến đã được xác định bên ngoài nó. Đây được gọi là đóng cửa. Nó cũng được sử dụng rất nhiều trong JavaScript.


Đoạn trích Một: var hero = {name: 'Rafaelo', sayName: function () {return hero.name; }, nayName: hero.sayName} hero.nayName (); Đoạn trích thứ hai: var hero = {name: 'Rafaelo', sayName: function () {return hero.name; }, nayName: this.sayName} hero.nayName (); Đoạn mã đầu tiên cho tôi kết quả chính xác trong khi đoạn mã thứ hai thì không. Tại sao? Trân trọng.
Cafecorridor

thischỉ có nghĩa là một cái gì đó bên trong một cơ thể hàm, nếu không nó là toàn cục. Những gì bạn đang nói this.sayNamelà bạn muốn biến toàn cục sayNamekhông tồn tại, nó không được xác định, vì vậy không thể gọi được.
kzh

7
Tôi đã bối rối bởi d () (); lúc đầu nhưng sau đó nhận ra rằng () đầu tiên gọi d và () thứ hai gọi giá trị trả về của d, là e.
skud

1
Tám năm sau và điều này vẫn còn phù hợp!
Brad Vanderbush

45

Trả về tên hàm mà không ()trả về tham chiếu đến hàm, có thể được gán như bạn đã làm với var s = a(). sbây giờ chứa một tham chiếu đến hàm b()và gọi s()hàm tương đương với gọi b().

// Return a reference to the function b().
// In your example, the reference is assigned to var s
return b;

Việc gọi hàm với ()trong một câu lệnh return sẽ thực thi hàm và trả về bất kỳ giá trị nào được hàm trả về. Nó tương tự như gọi var x = b();, nhưng thay vì gán giá trị trả về, b()bạn đang trả về nó từ hàm gọi a(). Nếu b()bản thân hàm không trả về giá trị, thì lệnh gọi sẽ trả về undefinedsau khi bất kỳ công việc nào khác được thực hiện b().

// Execute function b() and return its value
return b();
// If b() has no return value, this is equivalent to calling b(), followed by
// return undefined;

1
Trong tất cả các câu trả lời, tôi thích câu trả lời của bạn hơn do tính đơn giản của nó.
anar khalilov

Tốt! Rất dễ hiểu câu trả lời này.
AndrewSteinheiser

Cảm ơn bạn đã xác thực bit về việc nếu hàm không trả về giá trị thì cuộc gọi trả về không xác định. Tôi đã phát hiện ra điều lộn xộn này gần đây: ngay cả khi hàm trả về null rõ ràng, nếu bạn gán giá trị được trả về cho một biến, nó sẽ không được xác định, không phải null. Tôi tưởng tượng điều này gây ra rất nhiều vấn đề kỳ lạ trong một số cơ sở mã, bởi vì null và undefined không hoàn toàn tương đương với ===.
Benjamin

37

return b(); gọi hàm b () và trả về kết quả của nó.

return b; trả về một tham chiếu đến hàm b, mà bạn có thể lưu trữ trong một biến để gọi sau.


17

Trả về blà trả về một đối tượng hàm. Trong Javascript, các hàm chỉ là các đối tượng, giống như bất kỳ đối tượng nào khác. Nếu bạn thấy điều đó không hữu ích, chỉ cần thay thế từ "object" bằng "thing". Bạn có thể trả về bất kỳ đối tượng nào từ một hàm. Bạn có thể trả về giá trị true / false. Một số nguyên (1,2,3,4 ...). Bạn có thể trả về một chuỗi. Bạn có thể trả về một đối tượng phức tạp với nhiều thuộc tính. Và bạn có thể trả về một hàm. một chức năng chỉ là một thứ.

Trong trường hợp của bạn, trả bvề trả về thứ, thứ là một hàm có thể gọi. Việc b()trả về trả về giá trị được trả về bởi hàm có thể gọi.

Hãy xem xét mã này:

function b() {
   return 42;
}

Sử dụng định nghĩa trên, return b();trả về giá trị 42. Mặt khác return b;trả về một hàm, chính nó trả về giá trị 42. Chúng là hai thứ khác nhau.


4
nó sẽ trở lại 42;)
c69

4

Khi bạn trả về b, nó chỉ là một tham chiếu đến hàm b, nhưng không được thực thi tại thời điểm này.

Khi bạn quay trở lại b(), bạn đang thực thi hàm và trả về giá trị của nó.

Hãy thử alerting typeof(s)trong ví dụ của bạn. Đoạn mã b sẽ cung cấp cho bạn 'chức năng'. Đoạn mã a sẽ mang lại cho bạn điều gì?


Đầu tiên là "không xác định". Có nghĩa là return b () hoàn toàn vô dụng? Ngoài ra, trong đoạn mã thứ hai, hàm b là riêng tư. Làm thế nào sau đó chúng ta có thể truy cập tham chiếu bên ngoài hàm? Vui lòng cung cấp cho tôi một liên kết giải thích rõ ràng khái niệm này nếu có thể. Cảm ơn!
Cafecorridor

Tôi đã có câu trả lời cho câu đầu tiên. trả về 1 + 2 trong hàm b () và typeof hiển thị số. Cảm ơn.
Cafecorridor

Rất vui vì bạn đã tìm ra nó! Đối với hàm riêng tư: Nó không thực sự riêng tư trong ví dụ thứ hai vì bạn đã trả lại nó. Trên thực tế, nó được gán cho s. Hãy thử return thisthay vì return bmặc dù ... Bạn sẽ có thể để làm s.b()sau đó;)
vzwick

Tôi sẽ thử nó cho chắc chắn. Chưa đạt đến khái niệm này trong Javascript. Có thể trong một vài ngày. Cảm ơn! :)
Cafecorridor

function a () {alert ("A!"); function b () {alert ("B!"); } trả về b; } var s = a (); xóa một; S(); ---- end ---- Khái niệm tham chiếu trong Javascript có giống trong Java không? Ở đây tôi đã xóa hàm a () và một lệnh gọi đến s () thực thi b (). Vì vậy, tôi có thể nói rằng s chứa một bản sao của b và không trỏ đến b () được định nghĩa trong a () không?
Cafecorridor

2

Hãy tưởng tượng hàm như một kiểu, giống như một int. Bạn có thể trả về số nguyên trong một hàm. Bạn cũng có thể trả về các hàm, chúng là đối tượng của kiểu "hàm".

Bây giờ vấn đề về cú pháp: bởi vì các hàm trả về giá trị, làm thế nào bạn có thể trả về một hàm mà không phải nó trả về giá trị?

bằng cách bỏ qua dấu ngoặc! Bởi vì không có dấu ngoặc, hàm sẽ không được thực thi! Vì thế:

return b;

Sẽ trả về "hàm" (hãy tưởng tượng nó giống như nếu bạn đang trả về một số), trong khi:

return b();

Đầu tiên thực thi hàm sau đó trả về giá trị thu được bằng cách thực thi nó, đó là một sự khác biệt lớn!


Trong đoạn mã thứ hai, hàm b là riêng tư. Làm thế nào sau đó chúng ta có thể truy cập tham chiếu bên ngoài hàm? Có quy tắc nào quản lý giống nhau không?
Cafecorridor

Các đối tượng trong JavaScript (điều này bao gồm các hàm) không còn là riêng tư nếu bạn chia sẻ chúng.
kzh

@Cafecorridor: Nếu hàm private được trả về bởi một thứ gì đó (một hàm public) hoặc được gán cho một biến public (tốt, một biến có thể truy cập ứng dụng), bạn có thể dễ dàng thực hiện yourvariable (); , nếu không thì gán hàm trả về cho một biến và thực hiện lại yourvariable ();
Francesco Belladonna

2

Tạo một biến :

var thing1 = undefined;

Khai báo một hàm :

function something1 () {
    return "Hi there, I'm number 1!";
}

Cảnh báo giá trị của thing1(biến đầu tiên của chúng tôi):

alert(thing1); // Outputs: "undefined".

Bây giờ, nếu chúng ta muốn thing1trở thành một tham chiếu đến hàm something1, nghĩa là nó sẽ giống như hàm đã tạo của chúng ta, chúng ta sẽ làm:

thing1 = something1;

Tuy nhiên, nếu chúng ta muốn return giá trị của hàm thì chúng ta phải gán cho nó giá trị trả về của hàm được thực thi. Bạn thực thi hàm bằng cách sử dụng dấu ngoặc đơn:

thing1 = something1(); // Value of thing1: "Hi there, I'm number 1!" 

-1

Đoạn mã một:

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b(); //return nothing here as b not defined a return value
}

var s = a(); //s got nothing assigned as b() and thus a() return nothing.
alert('break');
s(); // s equals nothing so nothing will be executed, JavaScript interpreter will complain

câu lệnh 'b ()' có nghĩa là thực thi chức năng có tên 'b' hiển thị hộp thoại có văn bản 'B!'

câu lệnh 'return b ();' nghĩa là thực thi một hàm có tên là 'b' và sau đó trả về hàm 'b' nào. nhưng 'b' không trả về gì, thì câu lệnh 'return b ()' này cũng không trả về gì. Nếu b () trả về một số, thì 'return b ()' cũng là một số.

Bây giờ 's' được gán giá trị của giá trị trả về 'a ()', giá trị trả về 'b ()', là giá trị không có gì, vì vậy 's' không là gì (trong JavaScript thực sự là một thứ, đó là 'không xác định'. Vì vậy khi bạn yêu cầu JavaScript giải thích kiểu dữ liệu 's' là gì, trình thông dịch JavaScript sẽ cho bạn biết 's' là không xác định.) Vì 's' là không xác định, khi bạn yêu cầu JavaScript thực thi câu lệnh này 's ()', bạn đang yêu cầu JavaScript thực thi một hàm có tên là 's', nhưng 's' ở đây là 'không xác định', không phải là một hàm, vì vậy JavaScript sẽ phàn nàn, "này, s không phải là một hàm, tôi không biết làm thế nào để thực hiện với điều này ", thì thông báo lỗi" Uncaught TypeError: s không phải là chức năng "sẽ được hiển thị bằng JavaScript (được thử nghiệm trong Firefox và Chrome)


Đoạn trích hai

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b; //return pointer to function b here
}

var s = a();  //s get the value of pointer to b
alert('break');
s(); // b() function is executed

bây giờ, hàm 'a' trả về một con trỏ / bí danh cho một hàm có tên là 'b'. vì vậy khi thực thi 's = a ()', 's' sẽ nhận một giá trị trỏ đến b, tức là 's' là bí danh của 'b' bây giờ, gọi 's' tương đương với gọi 'b'. tức là 's' là một hàm bây giờ. Thực thi 's ()' có nghĩa là chạy hàm 'b' (giống như thực thi 'b ()'), một hộp thoại hiển thị 'B!' sẽ xuất hiện (tức là chạy câu lệnh 'alert (' B! '); trong hàm' b ')


-2

Điều này siêu hữu ích trong cuộc sống thực.

Làm việc với Express.js

Vì vậy, expresstuyến đường thông thường của bạn trông giống như sau:

function itWorksHandler( req, res, next ) {
  res.send("It works!");
}

router.get("/check/works", itWorksHandler );

Nhưng nếu bạn cần thêm một số trình bao bọc, trình xử lý lỗi hoặc smth thì sao?

Sau đó, bạn gọi hàm của mình ra khỏi một trình bao bọc.

function loggingWrapper( req, res, next, yourFunction ) {
  try {
    yourFunction( req, res );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", function( req, res, next ) {
  loggingWrapper( req, res, next, itWorksHandler );
});

Có vẻ phức tạp? Chà, thế này thì sao:

function function loggingWrapper( yourFunction ) => ( req, res, next ) {
  try {
    yourFunction( req, res, next );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", loggingWrapper( itWorksHandler ) );

Hãy xem ở phần cuối, bạn đang truyền một hàm loggingWrappercó một đối số là một hàm khác itWorksHandlervà bạn loggingWrappertrả về một hàm mới nhận req, res, nextlàm đối số.

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.