function-in-javascript

Function trong JavaScript

Ở bài trước mình đã nói về thứ đầu tiên của JS: Object. Trong bài này mình sẽ nói về Function, thứ mà đi code JS thì kiểu gì cũng phải xài tới, trong bài trước mình cũng dùng rồi đó.

Một điều hiển nhiên: Function là một object. Bạn nào thích một cái định nghĩa chi tiết về function thì đây:

“A function encloses a set of statements. Functions are the fundamental modular unit of JavaScript.”

– JavaScript the good parts

Tạo Function như thế nào?

Trong JS có một thứ được gọi là Function Literal (mình để nguyên gốc tiếng anh, bạn thích thì dịch ra hằng số hàm hay gì đó cho vui :v), hình dáng của nó như sau:

Một Function Literal có 4 phần:

  • Từ khoá function
  • Tên function (function name): Có thể có hoặc không, như cái ở trên kia là không có phần này nè. Nếu không có tên thì nó được gọi là anonymous.
  • Các tham số (parameters): Hàm có thể có tham số hoặc không, như trên kia là có 2 tham số a b. Các tham số đặt trong dấu ngoặc ( ), ngăn cách nhau bởi dấu ‘,’.
  • Các câu lệnh (statements): Được đặt trong dấu { }, như ở trên là có lệnh return a + b;

Một điểm đặc biệt là trong một function có thể định nghĩa một function khác (nested function)

Invocation (Gọi hàm)

Mình dùng từ tiếng anh ở đây để lỡ một ngày đẹp trời nào đó mình gặp lại nó thì còn biết đường mà hiểu. Gọi hàm ở trên kia được hiểu như là danh từ nhé (chính xác thì nó là “Sự gọi hàm”, nhưng đọc thấy kì bome luôn). Trong một trường hợp nào đó có thể bạn sẽ gặp các từ liên quan đến function như call, invoke, execute, … chúng nó đều có nghĩa là gọi hàm (ở đây là động từ), nhưng trong JS người ta thường dùng từ invoke (động từ) và invocation (danh từ).

Có cái từ như thế này: Additional parameters (bonus parameters). Trong JS thì khi gọi một hàm, hàm đó sẽ được nhận 2 cái additonal parameter đó là this và arguments, phần này sẽ nói về this, this là cái mẹ gì? Trong OOP thì this dùng để trỏ đến chính object đang gọi hàm, trong JS thì nó cũng có vai trò tương tự nhưng hơi lầy một xíu. This sẽ có giá trị tuỳ thuộc vào cái việc gọi hàm của mình nó thuộc cái mẫu gọi hàm nào (invocation pattern). This trong các mẫu này sẽ được gán giá trị theo những cách khác nhau.

Trong JS có 4 mẫu gọi hàm: Method invocation pattern, Function invocation pattern, Constructor invocation pattern, Apply invocation pattern. (Nếu không hiểu đoạn này thì bạn hãy bình tĩnh và đọc phần tiếp theo).

À còn một thứ mà mình thấy khá là sida trong việc gọi hàm của JS là nó không có lỗi run-time nếu như ta gọi hàm mà số lượng đối số truyền vào và số lượng tham số của hàm đó không bằng nhau. Nếu số lượng đối số ít hơn số lượng tham số thì nó gán undefined cho những đối số bị thiếu, nếu nhiều hơn thì nó bỏ bớt. JS nó cũng không check kiểu của đối số với tham số có khớp nhau không, như ở trên thay vì bạn gọi func(10, 11); mà bạn gọi func(“Lam”, “someone”); thì nó vẫn chạy như thường, cái này thì lợi ít hơn hại. ==> Bạn nên gọi hàm cẩn thận chứ không thì ngồi debug sấp mặt nếu như dính mấy trường hợp trên đó.

The Method Invocation Pattern

Bạn còn nhớ bài Object không, mấy cái hàm trong Object là method đó (showScore(), showInfo()). Khi một function là thuộc tính của một object nào đó thì nó được gọi là method. Như vậy mẫu gọi hàm Method Invocation có nghĩa là bạn gọi hàm là một thuộc tính của object, và trong mẫu gọi hàm này thì this mang giá trị là cái object đó (cái object mà đang gọi phương thức của nó ấy). Loại này thì lời gọi hàm của nó hay có dạng object.method hoặc object[“method”], như lamStudent.showInfo(); hoặc lamStudent[“showInfo”](); trong ví dụ ở bài trước.

The Function Invocation Pattern

Nếu function không phải là thuộc tính của một object thì nó được gọi hàm theo mẫu này, như là var sum = add(3, 4);

Với mẫu gọi hàm này thì this nó trỏ về global object (object toàn cục), đây là một chuyện khá sida trong JS việc này dẫn đến vấn đề khi ta sử dụng nested function như sau:

Ở đây thì student.showScore(); là gọi hàm theo Method Invocation pattern => this của showScore() sẽ là object student. Trong phương thức showScore lại có 1 hàm là cal_AVG(), nhưng lời gọi hàm var avg = cal_AVG(); lại thuộc mẫu Function Invocation => this của cal_AVG() là global object chứ không phải là student => kết quả như trên :v. Bonus cho bạn lúc debug luôn

function-debug

Nếu muốn sử dụng this mà không gặp vấn đề trên thì ta có thể gán this cho một biến khác rồi mang vào inner fuction để xài.

Kết quả ra ngon lành liền.

The Constructor Invocation Pattern

Nếu một hàm được gọi với từ khoá new thì một object mới được tạo và this sẽ trỏ đến object này. Bạn có thể xem ví dụ ở bài trước, phần sử dụng constructor để khởi tạo object.

Người ta hay viết hoa chữ cái đầu của constructor.

The Apply Invocation Pattern

Với 3 mẫu ở trên thì dễ dàng nhận thấy this auto nhận giá trị khi gọi hàm, còn với mẫu Apply Invocation này thì ta có thể set giá trị cho this. Xem đây

Để gọi phương thức apply thì phải tạo một mảng chứa các đối số cần truyền cho hàm. Apply có dạng apply(param1, param2), param1 là giá trị của this, param2 là mảng các đối số.

Quan sát mấy lời gọi hàm trên, đều làm object lamStudent thực hiện gọi hàm showInfo() nhưng kết quả có sự khác biệt, ở lời gọi 1 thì this auto là lamStudent, 3 4 thì this đã bị thay đổi giá trị.

Bên cạnh apply thì còn call và bind nữa, mình sẽ nói về chúng nó sau.

Arguments

Ở phần trên mình có nói về additional parameter gồm arguments và this. Đọc đến đây thì chắc bạn đã nắm rõ this, phần này mình sẽ giới thiệu về argument (giới thiệu để bạn biết nó thôi, sẽ có một bài viết sâu hơn về thứ này).

Argument là một object giúp hàm được gọi truy cập vào các đối số mà ta truyền vào cho hàm đó, có thể hiểu đơn giản là nó chứ các đối số mình truyền vào hàm đó.

Argument thuộc loại array-like. Nó giống mảng ở chỗ có thuộc tính length và có thể truy xuất các dữ liệu bằng chỉ số. Nhưng nó thiếu các phương thức khác của mảng như pop(), push(), …

Lấy ví dụ ở phần Apply Invocation Pattern nhé

argument-debug

Mình thêm dòng var x = arguments; để xuất hiện arguments :v, lời gọi hàm ở dòng có dấu chấm đỏ, nhìn cột bên trái, ta sẽ thấy object arguments, nó có các thuộc tính là callee (tham chiếu đến hàm đang thực thi, xem thêm ở: arguments.callee – Mozilla Dev), length (số các đối số truyền vào), các trường còn lại là giá trị các đối số ta truyền vào và __proto__ (một thuộc tính liên quan đến prototype của object).

Một trường hợp khá hữu ích của argument là ta có thể tạo ra các hàm với số lượng bất kì

argument-application

À, tuy arguments không có các phương thức của array nhưng ta vẫn có thể mượn các phương thức đó về cho arguments bằng cách sử dụng call/apply mà mình sẽ nói ở bài về mấy hàm này.

Bạn có thể xem nhiều hơn về arguments ở đây: The JavaScript arguments object…and beyond

Trả về (Return)

Return có tác dụng chuyển quyền điều khiển lại nơi mà chương trình gọi function để tiếp tục. Ta có thể sử dụng return để kết thúc sớm thực thực thi của function.

Một function luôn trả về một giá trị dù return có được gọi tường minh hay không. Nếu giá trị trả về không được xác định thì nó trả về undefined.

Nếu một function được gọi với từ khoá new (constructor invocatiob pattern), và nếu giá trị return của function đó không phải là object thì this sẽ được trả về (this trong trường hợp này mang giá trị là gì thì bạn xem lại phần trên nhé)

Cascade

Có một số phương thức có giá trị trả về là this (thường là các phương thức dùng để thay đổi trạng thái của object), ở các phương thức này ta có thể áp dụng kỹ thuật cascade, cascade là một khái niệm của JS cho phép một object gọi được nhiều phương thức trong một phát biểu duy nhất. For example :v

getElement(‘box’) sẽ quăng về cho ta một object, object này có thể gọi move() để thay đổi vị trí của nó, move() lại trả về chính object đã gọi nó, và như thế nó gọi tiếp width() …..

Bạn có thể hình dung nó như là

Thêm ví dụ nữa

Cascade giúp ta làm ngắn code trong một số trường hợp, một số tình huống phức tạp thì dùng cascade thì code nhìn ổn, đẹp và có nghĩa hơn, một số tình huống xài ẩu thì được bonus thêm bug 😐


Oke, cơ bản về function thì bài viết này đã đủ, mình sẽ nói về những thứ sâu hơn về function như scope, closure, callback, …. này nọ lọ chai trong các bài viết sau. Bạn nên đọc kĩ để hiểu được về function trước khi đụng chạm đến những vấn đề khác, không biết về function thì coi như xong :v. Giờ mình đi kiếm giấc ngủ cái đã.

Leave a Reply

Your email address will not be published. Required fields are marked *