Hướng dẫn invoke function javascript
JavaScript seriesChương hôm nay giới thiệu về function trong JavaScript, thứ tưởng đơn giản nhưng không đơn giản tí nào. Show
Nội dung chính
Nội dung chính
Bài viết này là một phần của series JavaScript dành cho người không mới, giúp các bạn đã có kinh nghiệm code trong các ngôn ngữ khác nhanh chóng làm quen với JS. Nếu được rất mong nhận được sự ủng hộ và đóng góp ý kiến của mọi người để hoàn thiện series. A. Function overview1. OverviewFunction (hàm) là một nhóm các câu lệnh có liên quan, được gom lại với nhau, được đặt tên và thực hiện một công việc gì đó. Sử dụng hàm giúp mã hạn chế bị trùng lặp, tăng tính tái sử dụng và tổ chức source code tốt hơn. Function có hai phần:
Function có thể nằm riêng lẻ, hoặc thuộc về một function hay object nào đó (function bên trong object gọi là method - phương thức). Function definition Cú pháp cơ bản định nghĩa một hàm.
Một định nghĩa function gồm có từ khóa function, tên function, theo sau là danh sách tham số (parameter - viết tắt param) trong cặp ngoặc tròn. Cuối cùng là thân hàm gồm các câu lệnh trong cặp {}. Function calling (invoking) Hàm có thể chạy bằng cách gọi hàm (call - invoke) hoặc để hàm tự động chạy (self invoking). Hàm tự động chạy sẽ được bàn sau. Hàm có thể dùng riêng lẻ như một statement, hoặc dùng trong một biểu thức (khi này hàm nên return giá trị nào đó).
Khi gọi hàm, cần truyền cho nó một số lượng đối số. Các đối số này đi vào hàm và được chuyển vào các param tương ứng. Trong JS không bắt buộc, và cũng không kiểm tra truyền đối số có đầy đủ hay không, do đó bạn có thể truyền thiếu, thừa đối số cũng không sao. Các param bị thiếu thì sẽ có value là undefined, hoặc default value nếu có. Chú ý, khi gọi hàm cần có ngoặc (), dù có truyền đối số hay không. Ví dụ như sau.
Nếu bạn chỉ ghi Return a value Khi gặp lệnh return, hàm sẽ thoát và trả về giá trị nếu có. Các lệnh còn lại sẽ không được thực hiện (ngoại trừ một số trường hợp như finally block).
Function hoisting Function được hoisting (kéo lên), tương tự các biến khai báo với var. Kĩ thuật hoisting đã được nói tới trong các chương trước, nên không bàn nhiều ở đây.
2. Function & eventsHTML event là những sự kiện xảy ra đối với element cụ thể, ví dụ như click vào button thì sự kiện onclick của button đó được gọi,... Tên event toàn bộ là chữ thường, và có dạng một thuộc tính của tag. JS inline Bên trong thuộc tính event của tag, có thể chứa những đoạn mã JS.
Khi event cụ thể được fire, thì code trong event đó được chạy. Vì code JS thường dài, nếu đặt hết vào thuộc tính event sẽ rắc rối, nên cách tốt nhất là dùng event để gọi một function đã định nghĩa từ trước.
Về các event, các bạn sẽ được tìm hiểu trong chương DOM. This keyword Để biết được element nào nhận được event, thì sử dụng từ khóa this. This trong một thuộc tính event mang ý nghĩa là element nhận được thuộc tính đó.
Thuộc tính Chú ý, khi gọi hàm trong event, thì this không sử dụng được trong hàm. Ví dụ như sau sẽ không chạy.
Giải pháp cho vấn đề này là truyền đối tượng this cho hàm
Event sẽ truyền this thành một tham số tên là 3. This keywordTừ khóa this trong JS thực sự có nhiều ý nghĩa, nhưng cũng dễ để nắm bắt được.
B. Definition & invocation1. Function definition (advanced)Function expression Ngoài cách khai báo (declare) hoặc định nghĩa (define) function bình thường như sau.
Còn có cách viết khác là dùng function dạng biểu thức (expression). Biểu thức function có thể gán vào một biến, và biến này sẽ có kiểu function.
Chú ý dòng 2, biểu thức gán cho biến
Function expression không được hoisting, vì bản thân nó là một value (vế phải dấu bằng), nên không được hosting. Thực ra biến vẫn được hoisting, nhưng chỉ là không dùng được như function (value là undefined), nên coi như nó không được hoisting. Function constructor Cách khác nữa để tạo hàm là dùng Function constructor.
Function constructor nhận vào nhiều tham số, trong đó tham số cuối là code body của function. Không nên dùng function constructor, vì code rối rắm và không an toàn. 2. Parameters & argumentsParameters & arguments Tham số (parameter - param) là những biến trong cặp () của function, đại diện cho những đối số được truyền vào trong hàm. Các param được coi như các biến cục bộ trong hàm, và bị hủy khi hàm thực hiện xong. Đối số (argument) là những biến, giá trị thực sự được truyền vào hàm. Các đối số được chuyển vào bên trong hàm, biến thành các tham số theo đúng thứ tự.
Người ta nhắc tới tham số khi ở bên trong định nghĩa hàm, và gọi là đối số khi gọi hàm. Và param phải là biến (biến object, biến function cũng là biến), trong khi đối số có thể là bất cứ thứ gì có giá trị, như số, biến, hằng, biểu thức, hàm,... Parameter rules JS không định kiểu cho tham số, và cũng không kiểm tra số lượng đối số truyền vào. Do đó, số argument truyền cho hàm có thể nhiều hơn, hoặc ít hơn số lượng param cần có. Những param không nhận được argument (do truyền bị thiếu) thì sẽ có value là undefined.
ES6 (ECMAScript 2015) cho phép function có default value cho param. Khi truyền không đủ đối số, thì những param bị thiếu thay vì có value là undefined, thì nó sẽ sử dụng default value.
Trong đoạn
code trên, số 10 truyền cho Argument passing Đây là khái niệm cực quan trọng khi học về hàm, trong mọi ngôn ngữ lập trình. Trong JS, nếu đối số là kiểu primitive thì được truyền pass by value (theo giá trị). Một bản sao của argument được tạo ra và đưa vào param, mọi thao tác trong hàm đều thực hiện trên bản sao nên dữ liệu gốc không bị ảnh hưởng (khi hàm thực hiện xong). Đối với đối số object, thì truyền kiểu pass by reference (tham chiếu). Thực ra vẫn là pass by value, nhưng value ở đây là tham chiếu tới địa chỉ bộ nhớ, nên các thay đổi trên tham chiếu thì cũng ảnh hưởng tới dữ liệu gốc. 3. Function invocationInvoking a function Để gọi (call - invoke) một function, gọi tên nó và truyền cho nó danh sách các đối số. Số lượng đối số không cần thiết phải tương ứng với tham số.
Nếu function thuộc về một object, thì function gọi là method (phương thức). Gọi method tương tự gọi hàm, nhưng phải có tên object và dấu chấm phía trước.
Thực ra mọi function trong JS đều là method, các function không thuộc object nào thực ra thuộc về object window. Do đó, ví dụ đầu tiên có thể viết lại như sau, cũng cho kết quả tương tự.
Khi function thuộc object, chúng ta gọi object là owner (chủ sở hữu) của function đó. Call function as a constructor Có thể gọi hàm với từ khóa new, lúc này function được coi như là một constructor. Constructor thường dùng để khởi tạo một object mới.
This trong trường hợp này không phải đối tượng window, mà là object mới được tạo ra nhờ từ khóa new. Object này sẽ được gán tham chiếu tới biến Vấn đề này sẽ được bàn kĩ hơn trong chương object. Self invoking function (IIFE) Một hàm có thể được tự động gọi mà không cần lời gọi hàm, chúng có tên là self invoking function, hoặc IIFE (Immediately invoke function expression).
Chú ý hai dòng trên, chúng ta có 2 cách để làm một function thành self invoking:
Self invoking function tự động chạy khi được định nghĩa. C. Function features1. Anonymous functionAnonymous function (hàm ẩn danh) là một hàm không có tên, đơn giản vậy thôi.
Hàm ẩn danh thường được dùng trong function expression, để viết nhanh một hàm chỉ dùng một lần. Anonymous function cũng được sử dụng làm callback, truyền dưới dạng tham số cho một hàm khác và được chính hàm đó gọi lại (call back) sau một khoảng thời gian làm gì đó . Ngoài ra nó còn được dùng trong closure hoặc cho self invoking function.2. Function objectFunction cũng là một object trong JS, cũng có những thuộc tính (property) và phương thức (method). Chúng ta chỉ quan tâm tới hai đối tượng cơ bản nhất. Method toString() Dùng chuyển một function thành string, nghĩa là show toàn bộ code của function đó.
Arguments object Bên trong mỗi function có một object ẩn là 3. Arrow functionArrow function (hàm mũi tên) là một cách viết khác ngắn gọn hơn cho function, được giới thiệu từ phiên bản ES6. Ví dụ bên dưới, 3 hàm tương tự nhau nhưng có cách viết khác nhau.
Cách 2 là syntax chuẩn của arrow function, dạng Rút gọn return Cách 3 rút gọn lệnh return. Nếu phần thân function chỉ có một lệnh return, thì có thể rút gọn theo cách bỏ ngoặc {}. Rút gọn một lệnh Nếu thân function chỉ có một lệnh (không phải return), thì có thể bỏ ngoặc {}.
Chú ý, nếu chỉ có một lệnh nhưng là return, thì phải bỏ return như cách 3 ở trên. Nếu để return luôn sẽ có lỗi.
Rút gọn tham số Nếu function có 1 tham số, thì ngoặc () có thể bỏ đi.
Nếu không có tham số nào, thì phải giữ lại cặp (), không được bỏ đi.
D. Call, apply & closure1. Call methodMỗi function đều có một method với tên Ví dụ như đoạn code sau.
Code trên định nghĩa hai object:
Bạn sẽ đặt câu hỏi "tại sao không truyền thẳng object vào method như một param?". Thực ra câu hỏi đó hoàn toàn có lý, và như vậy code chúng ta sẽ trông như sau.
Hai đoạn code trên tương tự nhau, cho ra kết quả giống nhau. Why call? Sự khác biệt nằm ở chỗ context của function thay đổi. Khi gọi function với
Nói chung phần này hơi trừu tượng, và thực tế không dùng nhiều như cách truyền object như param. Call and params Function được gọi với
Lúc này khi gọi Như trên code trên, thì khi gọi
2. Apply methodMethod
Function được ES6 cung cấp một cách khác là spread operator khá giống Sử dụng 3. Call, apply in strict modeBình thường, nếu gọi Nhưng trong strict mode, điều này
bị cấm. Khi dùng E. Closure1. OverviewClosure là một trong những khái niệm trừu tượng nhất trong JS, và rất khó để hiểu được và định nghĩa chính xác nó là gì.
Theo định nghĩa của MDN về closure, thì có thể hiểu như sau (tạm dịch).
Có vẻ khái niệm closure vẫn còn khá trừu tượng, nhưng đừng lo. Hãy đi vào các đoạn code ví dụ để hiểu hơn.
Bên trên là cấu trúc cơ bản của một closure. Theo định nghĩa phía trên, chúng ta áp dụng vào code để hiểu rõ hơn. Ở đây function Closure & scopes Khi nhắc tới closure phải nhắc tới scope. Như phân tích ở trên, function bình thường chỉ có hai scope, nhưng closure tạo ra tới 3 scope. Chính scope mới này tạo nên các tính chất đặc biệt của closure. Đặc điểm closure Một closure có hai đặc điểm sau:
2. Cấu trúc một closureClosure có cấu trúc gồm 2 phần:
Ví dụ như sau là một closure đầy đủ nhất.
Câu đố dành
cho bạn đây. Hàm Nếu câu trả lời là 3, hoặc 4 lần thì bạn nhầm rồi. Hàm Bạn sẽ bảo "chạy inner() đấy.Để
tớ phân tích kĩ hơn câu lệnh Code trên nên viết lại với những comment như sau cho dễ hiểu.
Yeah, đó chính là cách hoạt động của closure. Question 1 Câu hỏi đầu tiên được đặt ra ở đây là "code trên liên quan vẹo gì tới closure?". Hãy nhớ lại đặc điểm scope của một closure:
Rồi, bây giờ quay lại code phía trên. Để thực hiện 2 đặc điểm trên, đơn giản chỉ cần khai báo biến bên trong
Như trên đã phân tích, thì Function Đặc điểm 1: Đặc điểm 2: Đấy, bắt đầu thấy liên quan chưa. Question 2 Câu hỏi thứ 2 liên quan tới câu hỏi 1 mà các bạn có thể nghĩ đến, là "làm sao biến trong outer có thể được giữ lại khi outer chỉ thực hiện một lần rồi thôi?" Thực ra Có thể hiểu như nếu còn Nếu đi sâu vào thêm nữa thì nó liên quan tới cách JS thực hiện các function trong execution context, rồi call stack đủ thứ nên thôi dừng lại ở đây. 3. Rút gọn closureClosure như ví dụ trên vẫn khá dài, nên được rút gọn lại như sau.
Phiên
bản đầu tiên rút gọn đi hàm Tiếp theo, chúng ta thay vì viết hàm
4. ExampleVí dụ trên về closure của mình có lẽ không rõ nghĩa lắm, tuy có phân tích khá chi tiết nhưng thực sự nó không có nghĩa trong thực tế. Do đó, mình khuyến khích các bạn tìm hiểu thêm một số ví dụ về closure khác nữa, để nắm rõ hơn về closure vì đây là một khái niệm cực kì quan trọng. |