Tất cả về Function – JavaScript (p1)

23 tháng 04, 2018 – 8566 lượt xem

Mọi thứ bạn cần biết về Function – Javascript

Đệ quy

 Function là gì?

Function (hàm, chức năng), gọi chung là subprogram (chương trình con) có thể được gọi ở bên ngoài hoặc bên trong chính nó.

 

Nó bao gồm tập hợp các câu lệnh gọi là function body. Các giá trị có thể truyền đến một hàm, và một hàm có thể trả về giá trị. 

Bây giờ, với những ứng dụng tân tiến, những function hoàn toàn có thể là một chương trình hoàn hảo, chứ không phải là khái niệm tổng quát như ‘ ‘ subprogram ” nữa .

Có sự khác nhau giữa function và procedure ( thủ tục ) rằng sự lý tưởng của function nên trả về một giá trị còn procedure thì không ( giờ đây điều này hoàn toàn có thể biến hóa theo ngôn từ lập trình ) .Với tổng thể mọi điều hãy viết một ” function ” in ra chữ ” hello ” ở console .

Function không có tham số và không trả về bất cứ giá trị gì.  

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

Function ở trên không có một tham số nào, và không trả về một giá tri .Function ở trên hoàn toàn có thể được gọi hoặc được dẫn bởi câu lệnh dưới đây :

sayHello();

Hiện tại, bạn thích thì hoàn toàn có thể sử dụng dấu chấm phẩy ( ; ) hoặc hoàn toàn có thể chọn bỏ lỡ nó ( chúng tôi không tham gia vào cuộc tranh luận về việc sử dụng dấu ” ; ” hoặc không dùng trong JavaScript ) .Output của đoạn mã trên sẽ được in ra ở màn hình hiển thị console như sau :

Nếu bạn sử dụng chuẩn es6/es2015 cùng một chức năng các bạn có thể sử dụng arrow function:

const sayHello = () => {
  console.log("Hello !");
}

Sử dụng arrow function là một cách viết ngắn gọn để viết một function. Nó được gọi tương tự như như trên :

sayHello();

Một arrow function có cú pháp ngắn hơn cú pháp function thông thường, nó hoàn toàn có thể không có đối số, super hoặc new.target của nó .Những function này tương thích nhất cho những non-method function và chúng không hề sử dụng như những constructor .Có điều gì khi tôi nói, function ở trên không trả về giá trị gì ?Nếu tôi nỗ lực để tàng trữ tác dụng của function được gọi ở trên vào một biến nó sẽ nhận giá trị ” undefined ” .Ví dụ :

let message = sayHello();
// The below console.log will return undefined as the function 
// doesn't return any value.
console.log (message);

Function có tham số nhưng không trả về giá trị nào

Hãy viết một function có một tham số truyền vào nhưng không trả về giá trị nào như ví dụ dưới đây :

function log (message) {
  console.log (message);
}

Function ở trên có một tham số có tên là message và câu lệnh in ra giá trị của message trên màn hình hiển thị console .

Bạn có thể gọi function ở trên như sau:
 

// The below call to log() function, logs the output to the 
// and returns undefined.
log ("Hello JavaScript!");

Nếu function không return bất kỳ một giá trị nào một cách rõ ràng, thì mặc định khi gọi hàm sẽ trả về ” undefined “

 Function có một tham số và trả về một giá trị cụ thể

Chúng ta cùng viết một function có một tham số tên là number và trả về bình phương của số đó như sau :

function square(number) {
 return number * number;
}
console.log(square(2));

Kết quả thực thi function ở trên được hiện ra dưới đây :

Các function là first-class-objects

Trong khoa học máy tính, một ngôn từ lập trình tương hỗ những function có dạng first-class objects ,. Cụ thể, điều này có nghĩa là ngôn từ đó tương hỗ việc kiến thiết xây dựng mới những function trong quy trình thực thi chương trình, tàng trữ chúng trong cấu trúc tài liệu, truyền chúng như là đối số cho những function khác, và trả về chúng như thể những giá trị của function khác – theo wikipedia

Các Function là first-class objects hoàn toàn có thể được gán cho một biến cũng hoàn toàn có thể được truyền như một tham số. Chúng ta sẽ thấy điều này qua một ví dụ :

// Bạn có thể sử dụng từ khóa var hoặc let.  Tôi sử dụng const để cho biết 
// rằng đây là function không thể khai báo lại
const square = function (number) {
  return number * number;
}
console.log(square(2));

Viết lại function phía trên sử dụng arrow function .

const square = (number) => {
  return number * number;
}
console.log(square(2)); // Outputs: 4

 Function có thể có nhiều đối số (thực tế có thể có ‘n’ đối số)

Trên kim chỉ nan không có số lượng giới hạn đối số, nhưng thực tiễn thì có .

Tham khảo tài liệu của stack overflow theo link dưới đây để biết thêm một số thông tin

Stackoverflow : javascript functions, maximum no. of arguments

Làm thế nào để viết một function có thể truyền vào ‘n’ đối số?

Viết một function có tên là sum ( ) hoàn toàn có thể truyền ‘ n ‘ đối số và trả về tổng của những đối đã truyền đó

// Cách cũ
const sum = function () {
  let result = 0;
  for(let i = 0; i < arguments.length; i++) {
    result += arguments[i];
  }
  return result;
}

Chúng ta hoàn toàn có thể gọi function ở trên bằng những cách dưới đây :

console.log(sum(1,2));
console.log(sum(1,2,3,4));
console.log(sum(1,3,5,7,9));

Nó hoạt động như thế nào?

Nếu bạn nhìn vào function sum, nó không cần một tham số rõ ràng nào. Bây giờ, hãy tưởng tượng bạn đang thực thi function sum ( ) này một cách rõ ràng, điều gì là khó khăn vất vả để xác lập những tham số trước đó. Nếu bạn không biết hãy nhìn những tác dụng khi gọi function sum ( ) .

Nếu truyền vào 1 tham số tương ứng như sum(1) nó sẽ trả về 1.

Nếu truyền vào 2 tham số tương ứng như sum(1,2) nó sẽ trả về 3.

Nếu truyền vào 100 tham số tương ứng như sum(1,2,3,4..,99,100) nó sẽ trả về 5050.

Vì vậy, JavaScript đã cung cấp một object bí mật là "arguments", nó chứa toàn bộ tham số và có thể sử dụng trong bất kỳ function nào.

Lưu ý, object "arguments" không phải là một Array mà là một Array like object. Có nghĩa là bạn không thể gọi bất kỳ phương thức áp dụng cho mảng trên đối tượng arguments (Nếu bạn tò mò, hãy nghiên cứu thêm về vấn đề này).

Hoạt động của function sum ( ) được minh họa dưới đây .

Khi tôi nó rằng "arguments" là một array like object, hãy xem điều này ở hình dưới đây qua một lời gọi hàm. Quan sát console.log của arguments. Bạn thấy gì?

Hình ảnh ở trên minh họa rất rõ ràng rằng những đối số là một đối tượng người dùng tương ứng với những key là những value là những tham số được truyền vào .Object này cũng giống như bất kể những đối tượng người dùng khác, ví dụ :

{
   name:  "Rajesh",
   hobbies: ["writing","programming"]
}

Các key ở đối tượng argument giống như index của một mảng

{
   0:  "rajesh",
   1:  ["writing","programming"]
}

cả hai đều là đối tượng

Chú ý : JavaScript văn minh không khuyến khích sử dụng đối tượng người dùng " arguments ". Chúng ta sử dụng một khái niệm mới gọi là REST parameters

Hãy xem cách sử dụng REST parameters để đạt được kết quả như trên mà không cần sử dụng đối tượng arguments:

// Cách mới sử dụng REST parameter
const sum = function (...args) {
  let result = 0;
  for(let i = 0; i < args.length; i++) {
    result += args[i];
  }
  return result;
}

Trong cả hai function, mọi thứ đều giống nhau ngoại trừ đối tượng người dùng " arguments " được sửa chữa thay thế bằng " REST parameters " ( ... args ). Bây giờ bạn hoàn toàn có thể gọi nó là bất kể thứ gì, như một quy ước ở đây tôi gọi nó là " args " .

...args làm cái gì?

...args lấy mọi tham số truyền vào cho function và làm cho nó tồn tại dưới một mảng. Hãy nhớ rằng arguments là một đối tượng (array like object) còn ...args là một mảng.

Chúng ta liên tục lấy ví dụ, ở đây tất cả chúng ta gọi hàm sum ( ) một lần nữa và trong hàm sum ( ) tất cả chúng ta đặt một câu lệnh console.log như đoạn code dưới đây :

const sum = function (...args) {
  console.log(args);
  let result = 0;
  result = args.reduce((current, prev) => {
    return current + prev;
  });
  return result;
}
sum(1,2,3,4,5);

Output khi gọi hàm sum ( ) cùng với console.log được hiển thị bên dưới

Trong đoạn log ở trên vì args là một mảng, và vì là một mảng nên tất cả chúng ta hoàn toàn có thể sử dụng phương pháp reduce để tính tổng .

... args cũng sử dụng được một phần trong nhiều tham số truyền vào

Bây giờ tất cả chúng ta sẽ gọi hàm sum ( ) với 3 tham số sum ( 1,2,3 ) và output nhận được là :

Function lấy function như một tham số

Như tất cả chúng ta đã đề cập trước đó function là first-class object do đó chúng hoàn toàn có thể được truyền như thể tham số / đối số trong một hàm .Chúng ta sẽ viết một function truyền vào đó một tham số là function

function dispatch (fn) {
  fn();
}

Trong đoạn mã phía trên, chúng ta định nghĩa một hàm tên là dispatch để nhận một hàm làm tham số truyền vào. Lưu ý rằng tên "fn" chỉ là một quy ước, bạn có thể sử dụng nhiều tên khác. Tên thông dụng khác thường là callback nhưng được sử dụng trong một ngữ cảnh khác.

NOTE : chúng tôi sử dụng thuật ngữ " callback " khi chúng tôi đề cập đến những function có tham số là function

Bây giờ, làm thế nào để sử dụng những function trên ?Hãy gọi hàm ở trên theo ví dụ dưới đây. Bạn hoàn toàn có thể sử dụng cú pháp function thông thường hoặc dùng arrow function, ở đây tôi sẽ dùng arrow function :

//Cách 1: khai báo một function để làm tham số.
var fn = () => { console.log( "Hello !"); }
// gọi function dispatch()
dispatch(fn);  // Outputs "Hello !"

//Cách 2:  Khai báo một anonymous function bên trong
dispatch (function () {
  console.log("Hello !");
});

//METHOD 3:  Định nghĩa một arrow function bên trong
dispatch (() => { console.log ("Hello !") });

 Chú ý: 3 cách trên đều tương tự nhau

Callback function có thể truyền tham số vào và trả về giá trị. Hãy xem ví dụ ở dưới đây.

function dispatch(fn) {    // Truyền vào một function như là tham số
  return fn("hello");   
}

Function dispatch ở trên có đối số là một function và trả về giá trị trả về từ hàm được truyền vào. Nó cũng được gọi là hàm truyền vào với đối số là một function .Làm thế nào để gọi hàm này ?

let result = dispatch(function (p1) {
  return `My message and ${p1}`;
});

Hãy nghĩ về điều này một chút ít .

Ứng dụng thực tế của callback function

Giả sử tất cả chúng ta thực thi một số ít method sau khoảng chừng 1 giây và không phải là ngay lập tức. Ở đây tất cả chúng ta hoàn toàn có thể sử dụng ham setTimeout .

setTimeout(function () {
  console.log('kiểm tra trạng thái của vài server');
}, 1000);

Phương thức trên sẽ đợi tối thiểu 1 giây trước khi thực thi. Chú ý rằng thời hạn ở setTimeout và setInterval tính theo mili giây .Nếu bạn muốn thực thi mỗi method sau mỗi khoảng chừng thời hạn là 5 giây, bạn hoàn toàn có thể dùng hàm setInterval .

setInterval(()=> {
   console.log("Mỗi 5s sẽ in ra nội dung này");
}, 5000);

Trong những hàm này bạn hoàn toàn có thể viết bất kể đoạn mã nào, hoàn toàn có thể thực thi 1 số ít cuộc goi Ajax .

Chú ý : Tôi đang sử dụng cả hai cách viết là function thông thường và arrow function để kết thúc bài đọc, họ cảm thấy tự do với hai cách viết này

Function có thể gọi chính nó (giống như đệ quy) 

Đệ quy là một khái niệm mê hoặc trong đó một hàm gọi chính nó. Bây giờ nếu bạn không giải quyết và xử lý điều kiện kèm theo dừng đệ quy, function hoàn toàn có thể chạy vô hạn và sau cuối trình duyệt hoàn toàn có thể ném ra ngoại lệ tràn call stack như ' Maximum call stack size exceeded ' .Hãy xem một ví dụ :

function runForEver() {
    runForEver();
}
// Bạn có thể gọi hàm trên bằng dòng lệnh ở dưới
runForEver();   // Bạn sẽ nhận được lỗi về call stack

Hàm trên là một trình diễn đơn thuần của hàm đệ quy. Như tên function cho thấy, tính năng này sẽ chạy mãi mãi, cho đến khi lỗi trên được trình duyệt đưa ra .OK, đó là một function khá vô dụng .Bây giờ hãy viết một hàm có ích hơn :

function countDown(n) {
  console.log (n);
  if (n >= 1) {  //Điều kiện để kết thúc
    countDown(n-1);
  }
}

Bạn hoàn toàn có thể chạy function ở trên nhờ câu lệnh dưới đây .

countDown(5);  // -> Output sẽ là 5, 4, 3, 2, 1

Function đệ quy hoạt động giải trí như thế nào ?Từ sơ đồ trên, rất rõ ràng những cuộc gọi đệ quy tạo ra một ngăn xếp như thế nào và trong trường hợp tất cả chúng ta quên đặt điều kiện kèm theo để kết thúc thì stack sẽ tăng trưởng vô hạn và sau cuối bạn sẽ thấy lỗi “ Maximum call stack size exceeded ” .Hãy để chúng tôi thiết kế xây dựng một ví dụ trong thực tiễn hơn. Giả sử bạn có cấu trúc tài liệu bên dưới .

let data = [
  {
    title: "menu 1",
    children :[
      { title: "menu 1.1"},
      { 
        title: "menu 1.2",
        children: [
          {title: "menu 1.2.1"},
          {title: "menu 1.2.2"},          
        ]
      },
    ]
  },
  {
    title: "menu 2",
    children :[
      { title: "menu 2.1"},
      { title: "menu 2.2"},
    ]
  } 
]

Ở trên, chúng tôi có một cấu trúc phân cấp, hoàn toàn có thể đại diện thay mặt cho một menu hoặc bất kể điều gì bạn muốn .Chúng tôi muốn lấy điều này làm đầu vào và tạo list không theo thứ tự với cấu trúc phân cấp đúng chuẩn .

Hãy viết code cho function đệ quy của chúng ta để biến đổi dữ liệu thành các cặp

    ở trên.

Đầu tiên, hãy xem tất cả chúng ta định sử dụng function như thế nào .

let uls = buildTree(data);
// Output data to console
console.log(uls);
// Render on the window
// Only for demo. In real case append it to any parent element
// instead of using document.write
document.write(uls);  

Bây giờ tất cả chúng ta hãy triển khai function buildTree .

 

// Chấp nhận 2 đối số
// data-> data cần chuyển đổi
// isChild -> mặc định false, được sử dụng để cho biết liệu nút được render là phần tử con hay không

function buildTree(data, isChild = false) {
  let html = '
    '; // khởi tạo biến html // chạy vòng lặp for qua tất cả data data.forEach((d) => { // Mỗi phần tử data render ra một thẻ
  • html += `
  • ${d.title}
  • `; // Nếu phần tử hiện tại của data có phần tử con thì hãy gọi buildTree một lần nữa qua các thẻ children và isChild =true if (d.children) { html += buildTree(d.children, true); } }); // tạo thẻ đóng
html += '

';
return html; //trả về chuỗi html
}Còn tiếp ...Bài viết được dịch từ codeburst.ioĐăng ký thực tập Web front-end tại : http://bit.ly/2GTgkky