Lợi ích của hàm không có tham số chỉ gọi hàm khác là gì


21

Một hướng dẫn (cho Javascript) Tôi đang làm gợi ý chúng ta viết một hàm như thế này:

function sayHello() {
   //Some comments explaining the next line
   window.alert("Hello");
}

Khác với obfuscation có lợi ích gì khi viết một cái gì đó như thế này trong cuộc sống thực? Nếu vậy, những lợi ích là gì?


14
Có lẽ nó chỉ đang cố gắng chỉ cho bạn cách xác định các chức năng của riêng bạn.
Doval

4
Tôi sẽ không bao giờ viết mã theo cách như vậy để làm xáo trộn nó - mã là để lập trình viên dễ đọc, không phải là cách khác. Sử dụng một obfuscater cho obfuscation.
rhughes

32
Lợi ích là nó bao bọc một hàm với các tham số. Bằng cách này nếu sau này bạn muốn biến ứng dụng của mình thành tiếng Tây Ban Nha, bạn chỉ phải thay đổi "Xin chào" thành "Ola" ở một nơi.
Pieter B

9
@PieterB thực sự, "Hola"
rev

29
@PieterB - Và nếu bạn phát hiện ra mình viết sai chính tả "Hola", bạn chỉ phải sửa nó ở một nơi.
Daniel R Hicks

Câu trả lời:


60

Xin thứ lỗi cho bộ nhớ của tôi nếu tôi không đúng ... Javascript không phải là ngôn ngữ triển khai ưa thích của tôi.

Có một số lý do tại sao người ta muốn có một hàm arg bao bọc một lệnh gọi hàm khác. Trong khi cuộc gọi đơn giản window.alert("Hello");là một cái gì đó mà bạn có thể tưởng tượng thay vì gọi trực tiếp thay vì sayHello().

Nhưng nếu có nhiều hơn cho nó? Bạn đã có một tá địa điểm mà bạn muốn gọi sayHello()và đã viết window.alert("Hello");thay thế. Bây giờ bạn muốn nó làm một window.alert("Hello, it is now " + new Date()). Nếu bạn kết thúc tất cả các cuộc gọi khi sayHello()bạn thay đổi nó một nơi. Nếu bạn không, bạn thay đổi nó ở một chục nơi. Điều này chạm vào Đừng lặp lại chính mình . Bạn làm điều đó bởi vì bạn không muốn phải làm điều đó hàng chục lần trong tương lai.

Tôi đã làm việc với một thư viện i18n / l10n trong quá khứ đã sử dụng các hàm để thực hiện nội địa hóa phía máy khách của văn bản. Hãy xem xét sayHello()chức năng. Bạn có thể in nó ra holakhi người dùng được bản địa hóa sang tiếng Tây Ban Nha. Điều này có thể trông giống như:

function sayHello() {
  var language = window.navigator.userLanguage || window.navigator.language;
  if(language === 'es') { window.alert('Hola'); }
   else { window.alert("Hello"); }
}

Mặc dù, đây không phải là cách thư viện làm việc. Thay vào đó, nó có một tập hợp các tệp trông giống như:

# English file
greeting = hello
# Spanish file
greeting = hola

Và sau đó, thư viện sẽ phát hiện cài đặt ngôn ngữ trình duyệt và sau đó tạo các hàm động với nội địa hóa phù hợp làm giá trị trả về cho một lệnh gọi hàm không tranh cãi dựa trên tệp bản địa hóa thích hợp.

Tôi không đủ để một lập trình viên Javascript nói rằng điều đó tốt hay xấu ... chỉ là nó đã và có thể được coi là một cách tiếp cận khả thi.

Vấn đề là, việc gọi cuộc gọi đến một chức năng khác trong một chức năng của chính nó thường khá hữu ích và giúp mô đun hóa ứng dụng và cũng có thể giúp đọc mã dễ dàng hơn.

Tất cả những điều đó qua một bên, bạn đang làm việc từ một hướng dẫn. Nó là cần thiết để giới thiệu mọi thứ đơn giản nhất có thể khi bắt đầu. Giới thiệu các lệnh gọi hàm varargs từ đầu có thể dẫn đến một số mã rất khó hiểu cho một người không quen thuộc với mã hóa nói chung. Sẽ dễ dàng hơn nhiều khi đi từ không tranh luận, đến tranh luận, đến phong cách varargs - với mỗi tòa nhà trên các ví dụ và sự hiểu biết trước đó.


3
window.alertcũng thường được sử dụng như một trình giữ chỗ trong quá trình phát triển cho đến khi một phương thức / cửa sổ bật lên đẹp có thể được thiết kế / thực hiện, do đó, giống như vấn đề ngôn ngữ, nó có thể được bật ra dễ dàng hơn nhiều. Hoặc nếu bạn đã có, một bản cập nhật thiết kế vài năm nữa có thể yêu cầu thay đổi tương tự.
Izkata

34

Tôi nghĩ đôi khi nó hữu ích cho việc thực hiện ẩn.

 function sayHello() {
  window.alert("Hello");
 }

Và điều này cho phép bạn linh hoạt để thay đổi nó sau này

 function sayHello() {
  console.log("Hello");
 }

19

Khác với obfuscation có lợi ích gì khi viết một cái gì đó như thế này trong cuộc sống thực? Nếu vậy, những lợi ích là gì?

  • tập trung hóa: mặc dù việc triển khai là một dòng dài, nhưng nếu đó là một dòng thay đổi thường xuyên, bạn có thể thích thay đổi nó ở một nơi duy nhất, hơn bất cứ nơi nào nóiHello được gọi.

  • giảm thiểu / ẩn các phụ thuộc: mã khách hàng của bạn không còn cần phải biết có một đối tượng cửa sổ và thậm chí bạn có thể thay đổi toàn bộ việc thực hiện mà không ảnh hưởng đến mã khách hàng

  • thực hiện hợp đồng: mã khách hàng có thể mong đợi một mô-đun có chức năng sayHello. Trong trường hợp này, ngay cả khi tầm thường, thì chức năng phải ở đó.

  • tính nhất quán của các mức độ trừu tượng: nếu mã máy khách sử dụng các hoạt động mức cao, bạn nên viết mã máy khách theo các hàm 'sayHello, sayBye' và một số hàm 'sayXXX' khác, thay vì các đối tượng cửa sổ. Nguyên vẹn, trong mã máy khách, bạn thậm chí có thể không muốn biết có một thứ như một đối tượng 'cửa sổ'.


Tôi thích câu trả lời này. Thiết kế thường là lý do quan trọng hàng đầu cho các chức năng này, đặc biệt là thiết kế OO nơi bạn muốn các đối tượng chịu trách nhiệm về hành vi thực thi chức năng của các đối tượng khác với hành vi cụ thể. Hình ảnh chiếc xe của bạn, đặc biệt hơn là bạn chuyển số. Bạn chuyển thiết bị của mình lên bằng cách "nói" cây gậy của bạn chuyển lên. Đổi lại, nó chuyển tiếp cuộc gọi này đến hộp số của bạn. Nhưng ngoài ra, trước tiên nó kiểm tra bạn có thể chuyển lên từ vị trí hiện tại của bạn.
Eric

3
Các lý do khác là tốt nhưng +1 để đề cập đến mức độ trừu tượng. Sử dụng trừu tượng rõ ràng và tên hàm tốt có thể giúp đọc và duy trì mã cũ dễ dàng hơn nhiều. Tại thời điểm mà sayHello () được gọi, bạn không nhất thiết phải quan tâm đến cách nó được triển khai, bạn chỉ muốn hiểu ý định của dòng mã này là gì. Và một tên hàm như sayHello () làm cho điều đó trở nên rõ ràng.
kkrambo

Ngoài ra +1 để đề cập đến các mức độ trừu tượng: có thể xảy ra việc triển khai một hàm ở một mức độ trừu tượng bao gồm một lệnh gọi đến một hàm khác ở mức độ trừu tượng thấp hơn. Vậy thì sao?
Giorgio

7

Thật ngạc nhiên khi không một người nào khác đã đề cập đến thử nghiệm.

Dòng "bọc" cụ thể mà bạn đã chọn, window.alert('hello')thực sự là một ví dụ hoàn hảo về điều này. Bất cứ điều gì liên quan đến windowđối tượng là thực sự, thực sự đau đớn để kiểm tra. Nhân số đó với 1000 lần trong ứng dụng của bạn và tôi đảm bảo rằng các nhà phát triển cuối cùng sẽ từ bỏ thử nghiệm. Mặt khác, thật dễ dàng để chỉ chặn sayHellochức năng với một điệp viên và kiểm tra xem nó đã được gọi.

Một ví dụ thực tế hơn - bởi vì thực sự, ai thực sự sử dụng window.alert(...)trong mã sản xuất? - đang kiểm tra đồng hồ hệ thống. Vì vậy, ví dụ, điều này sẽ được gói DateTime.Nowtrong .NET, time(...)trong C / C ++ hoặc System.currentTimeMillis()Java. Bạn thực sự muốn bọc những người trong một phụ thuộc mà bạn có thể tiêm, bởi vì họ không chỉ (gần như) không thể chế giễu / giả mạo, họ chỉ đọc và không xác định . Bất kỳ thử nghiệm nào bao gồm một chức năng hoặc phương pháp sử dụng trực tiếp chức năng đồng hồ hệ thống đều có khả năng bị các lỗi không liên tục và / hoặc ngẫu nhiên.

Trình bao bọc thực tế là một hàm 1 dòng - return DateTime.Now- nhưng đó là tất cả những gì bạn cần để có được một đối tượng được thiết kế kém, không thể kiểm tra và làm cho nó trở thành một đối tượng sạch và có thể kiểm tra được. Bạn có thể thay thế đồng hồ giả cho trình bao bọc và đặt thời gian của nó thành bất cứ điều gì bạn muốn. Vấn đề được giải quyết.


2

Như Doval đã nói, ví dụ này có lẽ chỉ là cố gắng giới thiệu cho bạn các chức năng. Tôi nói chung, mặc dù, nó là hữu ích. Đặc biệt bằng cách chỉ định một số nhưng không phải tất cả thực hiện các đối số và chuyển các đối số khác qua, bạn có thể tạo một hàm cụ thể hơn cho từng trường hợp từ một hàm tổng quát hơn. Như một ví dụ tầm thường, hãy xem xét một hàm sắp xếp lấy một mảng để sắp xếp và một hàm so sánh để sắp xếp. Bằng cách chỉ định một hàm so sánh so sánh theo giá trị số, tôi có thể tạo một hàm sortByNumericalValue và các lệnh gọi đến hàm đó rõ ràng và súc tích hơn nhiều.


2

Suy nghĩ đằng sau câu hỏi của bạn dường như là: "Tại sao không viết alert("Hello");trực tiếp? Nó khá đơn giản."

Câu trả lời là, một phần, vì bạn không thực sự muốn gọi alert("Hello")- bạn chỉ muốn nói xin chào.

Hoặc: Tại sao lưu danh bạ trên điện thoại của bạn, khi bạn chỉ có thể quay số điện thoại? Bởi vì bạn không muốn nhớ tất cả những con số đó; bởi vì quay số là tẻ nhạt và dễ bị lỗi; bởi vì số có thể thay đổi, nhưng nó vẫn là cùng một người ở đầu kia. Bởi vì bạn muốn gọi người , không phải số.

Đây là những gì được hiểu bởi các thuật ngữ như trừu tượng hóa, gián tiếp, "ẩn chi tiết triển khai" và thậm chí là "mã biểu cảm".

Lý do tương tự áp dụng cho việc sử dụng hằng số thay vì viết các giá trị thô ở mọi nơi. Bạn chỉ có thể viết 3.141592...mỗi khi bạn cần số pi, nhưng rất may là có Math.PI.

Chúng tôi cũng có thể nhìn vào alert()chính nó. Ai quan tâm làm thế nào nó xây dựng và hiển thị hộp thoại cảnh báo đó? Bạn chỉ muốn cảnh báo người dùng. Giữa bạn viết alert("Hello")và các pixel trên màn hình của bạn thay đổi, có một chồng mã và phần cứng sâu, sâu, với mỗi lớp cho biết lớp tiếp theo nó muốn gì và lớp tiếp theo sẽ chăm sóc các chi tiết cho đến khi lớp sâu nhất có thể lật một số bit trong bộ nhớ video.

Bạn thực sự không muốn phải làm tất cả những điều đó chỉ để nói xin chào.

Lập trình - tốt, bất kỳ nhiệm vụ, thực sự - là tất cả về việc phá vỡ các vấn đề phức tạp thành các phần có thể quản lý được. Việc giải quyết từng đoạn cho bạn một khối xây dựng và với đủ các khối xây dựng đơn giản, bạn có thể xây dựng những thứ lớn.

Đó là đại số.


-1

Đó là một ý tưởng tốt để tách biệt các giá trị (biểu thức) khỏi việc thực hiện các hành động (câu lệnh). Chúng tôi muốn kiểm soát chính xác vị trí và thời điểm sẽ thực hiện các hành động (như hiển thị thông báo), nhưng khi tính toán các giá trị, chúng tôi muốn làm việc ở mức độ trừu tượng hơn và không phải quan tâm đến cách tính các giá trị đó.

Hàm chỉ tính toán giá trị trả về, chỉ sử dụng các đối số mà nó đưa ra, được gọi là thuần túy .

Một "chức năng" thực hiện một hành động thực sự là một thủ tục , có tác dụng .

Bất kỳ hiệu ứng nào gây ra trong quá trình tính toán giá trị đều được gọi là tác dụng phụ và tốt hơn hết là tránh chúng ở những nơi có thể ("Tôi chỉ cần chuỗi đó, tôi không biết nó sẽ làm hỏng cơ sở dữ liệu!").

Để giảm thiểu khả năng xảy ra tác dụng phụ, chúng ta nên tránh gửi quá nhiều dữ liệu cho các thủ tục của mình hoặc đặt bất kỳ phép tính nào vào chúng; nếu một số tính toán cần phải được thực hiện trước đó, tốt hơn là nên thực hiện riêng rẽ trong một hàm thuần túy, sau đó chỉ chuyển kết quả được yêu cầu cho thủ tục. Điều này giữ cho mục đích của thủ tục rõ ràng và giảm khả năng nó sẽ được sử dụng lại sau này như một phần của phép tính (thay vào đó, hàm thuần túy có thể được sử dụng lại).

Vì lý do tương tự, chúng ta nên tránh xử lý kết quả bên trong một quy trình. Tốt hơn là trả lại kết quả (nếu có) hành động của chúng tôi và thực hiện bất kỳ xử lý tiếp theo nào với các hàm thuần túy.

Nếu chúng tôi tuân theo các quy tắc này, chúng tôi có thể kết thúc bằng một quy trình như sayHellokhông cần dữ liệu và không có kết quả. Do đó giao diện tốt nhất cho nó là không có đối số và không trả về giá trị. Điều này tốt hơn là, ví dụ, gọi "console.log" ở giữa một số tính toán.

Để giảm nhu cầu về hiệu ứng trong quá trình tính toán, chúng ta có thể có các phép tính trả về các thủ tục ; ví dụ. nếu chúng ta cần quyết định thực hiện một hành động, chúng ta có thể có một hàm thuần túy chọn một thủ tục và trả về nó, thay vì thực hiện trực tiếp.

Tương tự như vậy, để giảm nhu cầu tính toán trong các thủ tục, chúng ta có thể có các thủ tục lấy các thủ tục khác làm tham số (có thể là kết quả của hàm); ví dụ. thực hiện một loạt các thủ tục và chạy cái khác


Bạn dường như đang ủng hộ OOP ở đây, nơi nguyên tắc cơ bản là "nói, đừng hỏi". Làm theo lời khuyên ở đây là những gì thường dẫn đến mã bị đánh đố với các lệnh gọi "getFoo", "setFoo" và "thực thi" vô dụng. OOP là về việc kết hợp dữ liệu và hành vi, không tách rời chúng.
Aaronaught

@Aaronaught Đúng là tôi chống lại OOP, nhưng tôi chắc chắn sẽ không đề xuất setFoocác cuộc gọi, vì điều đó ngụ ý dữ liệu có thể thay đổi. Việc thay đổi nội dung của các biến / thuộc tính là một hiệu ứng, khiến cho tất cả các tính toán sử dụng dữ liệu đó trở nên không trong sạch. Tôi sẽ không đề xuất executecác cuộc gọi mỗi lần, nhưng tôi sẽ đề xuất runFoocác quy trình để thực hiện các hành động dựa trên lập luận của họ.
Warbo

-2

Tôi muốn nói rằng đó là sự kết hợp của các câu trả lời được đưa ra bởi @MichaelT và @Sleiman Jneidi.

Có lẽ có một số vị trí trong mã nơi bạn muốn chào người dùng bằng tin nhắn Hello để bạn đóng gói ý tưởng đó trong một phương thức. Trong phương pháp, bạn có thể dịch hoặc mở rộng trên một chữ 'Xin chào' đơn giản bằng cách hiển thị một văn bản dài hơn. Ngoài ra, bạn có thể muốn sử dụng hộp thoại JQuery đẹp thay cho cảnh báo.

Vấn đề là việc thực hiện ở một nơi. Bạn chỉ cần thay đổi mã ở một nơi. (SayHello () có lẽ là một ví dụ quá đơn giản)


@downvoter (s) - quan tâm chia sẻ kiến ​​thức của bạn với phần còn lại của chúng tôi. Là bài viết của tôi chỉ đơn giản là sai, viết xấu hoặc những gì?
paul
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.