Tôi có nên đặt các chức năng chỉ được sử dụng trong một chức năng khác, trong chức năng đó không?


30

Cụ thể, tôi đang viết bằng JavaScript.

Giả sử chức năng chính của tôi là Chức năng A. Nếu Chức năng A thực hiện một số cuộc gọi đến Chức năng B, nhưng Chức năng B không được sử dụng ở bất kỳ nơi nào khác, vậy tôi có nên đặt Chức năng B trong Chức năng A không?

Đó có phải là thực hành tốt? Hay tôi vẫn nên đặt Hàm B ở cùng phạm vi với Hàm A?

Câu trả lời:


32

Tôi thường ủng hộ các hàm lồng nhau, đặc biệt là JavaScript.

  • Trong JavaScript, cách duy nhất để hạn chế khả năng hiển thị của một chức năng là lồng nó vào bên trong một chức năng khác.
  • Hàm trợ giúp là một chi tiết thực hiện riêng tư. Đặt nó ở cùng một phạm vi giống như làm cho các chức năng riêng tư của một lớp trở nên công khai.
  • Nếu hóa ra nó được sử dụng phổ biến hơn, thì thật dễ dàng để di chuyển ra ngoài, bởi vì bạn có thể tin rằng người trợ giúp hiện chỉ được sử dụng bởi một chức năng đó. Nói cách khác, nó làm cho mã của bạn gắn kết hơn.
  • Bạn thường có thể loại bỏ các tham số, điều này làm cho chữ ký hàm và triển khai ít dài dòng hơn. Đây không phải là điều tương tự như làm cho các biến toàn cầu. Nó giống như sử dụng một thành viên lớp trong một phương thức riêng tư.
  • Bạn có thể cung cấp cho các chức năng trợ giúp tốt hơn, tên đơn giản hơn, mà không phải lo lắng về xung đột.
  • Hàm helper dễ tìm hơn. Vâng, có những công cụ có thể giúp bạn. Vẫn dễ dàng hơn để di chuyển mắt lên màn hình một chút.
  • Mã tự nhiên hình thành một loại cây trừu tượng: một vài hàm chung ở gốc, phân nhánh thành một số chi tiết thực hiện. Nếu bạn sử dụng một trình soạn thảo với chức năng gập / thu gọn, lồng nhau sẽ tạo ra một hệ thống phân cấp các chức năng liên quan chặt chẽ ở cùng mức độ trừu tượng. Điều đó làm cho nó rất dễ dàng để nghiên cứu mã ở mức bạn cần và ẩn các chi tiết.

Tôi nghĩ rằng rất nhiều sự phản đối xuất phát từ thực tế là hầu hết các lập trình viên đều được nuôi dưỡng theo truyền thống C / C ++ / Java hoặc được dạy bởi một người khác. Các hàm lồng nhau trông không được tự nhiên vì chúng ta không tiếp xúc với chúng nhiều khi chúng ta học lập trình. Điều đó không có nghĩa là chúng không hữu ích.


14

Bạn nên đặt nó ở phạm vi toàn cầu, vì nhiều lý do.

  • Lồng một hàm trợ giúp vào người gọi làm tăng độ dài của người gọi. Chiều dài chức năng hầu như luôn luôn là một chỉ báo tiêu cực; các chức năng ngắn dễ hiểu hơn, ghi nhớ, gỡ lỗi và duy trì.

  • Nếu chức năng của trình trợ giúp có một tên hợp lý, đọc tên đó là đủ mà không cần phải xem định nghĩa gần đó. Nếu bạn làm cần phải xem định nghĩa helper để hiểu các chức năng gọi, sau đó người gọi đang thực hiện quá nhiều, hoặc đang làm việc trên quá nhiều cấp độ trừu tượng cùng một lúc.

  • Có người trợ giúp có sẵn trên toàn cầu cho phép các chức năng khác gọi nó nếu nó biến nó trở nên hữu dụng sau tất cả. Nếu người trợ giúp không có sẵn, bạn muốn cắt và dán nó, hoặc quên nó và thực hiện lại nó, kém, hoặc để làm cho một chức năng khác lâu hơn nó phải có.

  • Việc lồng chức năng của trình trợ giúp làm tăng sự cám dỗ khi sử dụng các biến từ phạm vi của trình gọi mà không cần khai báo, do đó nó không rõ ràng đầu vào và đầu ra của trình trợ giúp là gì. Nếu một chức năng không nêu rõ dữ liệu mà nó hoạt động và tác động của nó, thì đó thường là dấu hiệu của các trách nhiệm không rõ ràng. Khai báo ass helper một chức năng độc lập buộc bạn phải biết chính xác những gì nó thực sự làm.

Chỉnh sửa

Đó hóa ra là một câu hỏi gây tranh cãi hơn tôi nghĩ. Làm rõ:

Trong JavaScript, các hàm mở rộng tệp lớn thường hoàn thành vai trò của các lớp vì ngôn ngữ không cung cấp bất kỳ cơ chế giới hạn phạm vi nào khác. Chắc chắn các hàm trợ giúp nên đi vào bên trong các lớp gần như vậy, chứ không phải bên ngoài chúng.

Và điểm về giả thiết có tái sử dụng dễ dàng hơn rằng nếu một chương trình con không trở thành sử dụng rộng rãi hơn, bạn có sẵn sàng để di chuyển nó ra khỏi vị trí hoàn toàn và đặt nó ở vị trí thích hợp, ví dụ như một thư viện chuỗi tiện ích hoặc vào registry cấu hình toàn cầu của bạn. Nếu bạn không muốn đặt mã của mình như vậy, thì bạn cũng có thể lồng chương trình con, giống như bạn làm với một Đối tượng phương thức bình thường bằng ngôn ngữ "khối" hơn.


Cảm ơn, tất cả các điểm rất tốt. Bên cạnh đó, tôi nên đặt hàng các chức năng của mình như thế nào? Ví dụ, hiện tại, với các chương trình nhỏ, tôi chỉ cần sắp xếp chúng theo thứ tự bảng chữ cái. Nhưng tôi có nên nhóm các chức năng của người trợ giúp và người gọi không? Tôi đoán tôi ít nhất nên phá vỡ các chức năng của mình dựa trên những phần nào của ứng dụng mà chúng áp dụng, chẳng hạn?
Gary

Tôi đã không bận tâm đến việc định nghĩa phương pháp đặt hàng trong một thời gian dài. Nếu bạn lập trình với bất kỳ mức độ chuyên nghiệp nào, bạn nên có một môi trường cho phép bạn chuyển sang bất kỳ định nghĩa nào với một vài lần nhấn phím. Do đó, các phương thức ngắn là hữu ích, nhưng các tệp lớp ngắn hoặc thậm chí được sắp xếp tốt sẽ thêm ít giá trị. (Tất nhiên, JavaScript được sử dụng để yêu cầu tất cả các định nghĩa được đặt lên trên mức sử dụng của chúng hoặc kết quả về mặt kỹ thuật sẽ là hành vi không xác định - không thể nhớ lại nếu đó vẫn là trường hợp hay không.)
Kilian Foth

2
Đây là một câu trả lời tuyệt vời. Vấn đề đóng gói bị hỏng là điều mà nhiều người không cân nhắc khi sử dụng các chức năng bên trong.
sbichenko

2
Globals là một mùi mã. Chúng gây ra sự ghép đôi không cần thiết, ngăn chặn tái cấu trúc, chúng gây ra xung đột tên, chúng làm lộ chi tiết triển khai, v.v. Điều này đặc biệt tệ trong Javascript, vì tất cả các biến đều có thể thay đổi, mã thường được tải trực tiếp từ bên thứ ba, chưa (chưa) một hệ thống mô-đun có sẵn rộng rãi, hoặc thậm chí là triển khai "cho phép" rộng rãi. Trong một môi trường thù địch như vậy, chiến lược phòng thủ duy nhất chúng ta có là đóng gói bên trong các chức năng một phát.
Warbo

1
"Hoàn thành vai trò của các lớp" đang cố gắng viết JS như thể nó là một cái gì đó khác. "Vai trò của các lớp" là gì? Kiểm tra loại? Tính mô đun? Dàn dựng tổng hợp? Di sản? Câu hỏi liên quan đến phạm vi, vì vậy tôi giả sử bạn có nghĩa là các quy tắc phạm vi thô của các lớp. Điều đó chỉ vượt qua giới hạn: các lớp nên được phân chia như thế nào? PHP làm cho chúng toàn cầu, không cung cấp đóng gói. Python phạm vi các lớp theo từ vựng, nhưng nếu bạn ở trong một khối có phạm vi từ vựng, tại sao lại bọc mọi thứ trong một khối khác, trong phạm vi lớp? JS không có sự dư thừa như vậy: chỉ cần sử dụng phạm vi từ vựng nhiều như bạn cần.
Warbo

8

Tôi sẽ đề xuất một con đường thứ ba, để đặt cả hai chức năng trong một bao đóng. Nó sẽ trông như:

var functionA = (function(){
    function functionB() {
        // do stuff...
    }

    function functionA() {
        // do stuff...
        functionB();
        // do stuff...
    }

    return functionA;
})();

Chúng tôi tạo ra bao đóng bằng cách gói khai báo của cả hai hàm trong IIFE . Giá trị trả về của IIFE là hàm công khai, được lưu trữ trong một biến của tên cho hàm. Hàm công khai có thể được gọi theo cách chính xác giống như khi nó được khai báo là hàm toàn cục, nghĩa là functionA(). Lưu ý rằng giá trị trả về là hàm , không phải là lệnh gọi hàm, do đó không có parens ở cuối.

Bằng cách gói hai hàm như vậy, functionBgiờ đây hoàn toàn riêng tư và không thể truy cập bên ngoài bao đóng, nhưng chỉ hiển thị cho functionA. Nó không làm lộn xộn không gian tên toàn cầu không làm lộn xộn định nghĩa của functionA.


0

Xác định hàm B trong hàm A cung cấp cho hàm B quyền truy cập vào các biến nội bộ của A.

Vì vậy, một hàm B được xác định trong một hàm A mang lại ấn tượng (hoặc ít nhất là khả năng sở hữu) mà B đang sử dụng hoặc thay đổi các biến cục bộ của A. Trong khi xác định B bên ngoài A làm rõ rằng B không.

Do đó, để rõ ràng về mã, tôi sẽ chỉ xác định B trong A nếu B cần truy cập các biến cục bộ của A. Nếu B có mục đích rõ ràng là độc lập với A, tôi chắc chắn sẽ xác định nó bên ngoài A.


-1

Bạn không nên lồng nó, bởi vì nếu không thì hàm lồng nhau sẽ được tạo lại mỗi khi bạn gọi hàm ngoài.


-3

Vì hàm B không được sử dụng ở bất kỳ nơi nào khác, bạn cũng có thể biến nó thành một chức năng bên trong chức năng A vì đó là lý do duy nhất tại sao nó tồn tại.


1
điều này dường như không thêm bất cứ điều gì đáng kể vào các điểm được thực hiện và giải thích trong 3 câu trả lời trước
gnat
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.