Definition
Self-invoking function (hàm tự thực thi) có bản chất là các hàm vô danh (anonymous function1), được tự động truyền tham số và thực thi ngay khi chúng được khai báo. Self-invoking function còn có tên gọi khác là IIFE (Immediately Invoked Function Expression).
Nếu ta nhập lần lượt (123)
, ('Hello World')
, ([1, 2, 3])
và (function (){})
vào console ở browser thì chúng sẽ tự động được in ra.
(123) // 123
('Hello World') // 'Hello World'
([1, 2, 3]) // [1, 2, 3]
(function(n){console.log(n)}) // ƒ (n){console.log(n)}
Có thể thấy, dòng lệnh (function (n){console.log(n)})
sẽ trả về một function, ta có thể dùng toán tử ()
để gọi function này.
Thay đổi một chút hàm ở trên
(function(n) {
console.log(n) // 10
})(10)
Như vậy, ta đã có một self-invoking function.
Arrow Function as a Self-invoking Function
Cũng có thể dùng arrow function làm một self-invoking function:
((a, b) =>{
let sum = a + b
console.log(`Sum of ${a} and ${b} is ${sum}`) // Sum of 10 and 11 is 21
return sum
})(10, 11)
Assign Return Value of Self-invoking Function
Ta có thể gán giá trị trả về của self-invoking function cho một biến:
let four = 4
let square = (function(n) {
return n * n
})(four)
console.log(`Square of ${four} is ${square}`) // 16
Type Error
Xét đoạn code dưới đây:
let name = "Quan"
(function(){
}) // Uncaught TypeError: "Quan" is not a function
Ta đã biết toán tử ()
chính là toán tử gọi hàm, do đó, đoạn code trên khi đưa vào console nó sẽ hiểu là:
let name = "Quan"(function(){}) // Uncaught TypeError: "Quan" is not a function
Để sử dụng self-invoking trong trường hợp này, ta cần đặt dấu ;
ở đầu self-invoking function:
let name = "Quan"
;(function(name){
console.log(name)
})(name) // Quan
Scope of Self-invoking Function
Self-invoking function có phạm vi truy cập là “private”, hay nói cách khác là nó sẽ tạo ra một phạm vi riêng của nó, không thể truy cập từ bên ngoài:
(function square(n) {
console.log(`Square of ${n} is ${n * n}`)
})
square(4) // Uncaught TypeError: square is not a function
Tuy nhiên, vẫn có thể truy cập được trong phạm vi của hàm, chẳng hạn như khi sử dụng đệ quy:
let factorial = (function calcFactorial(n){
return n < 2 ? 1 : n * calcFactorial(n - 1)
})(3)
console.log(factorial) // 6
console.log(calcFactorial(3)) // Uncaught ReferenceError: calcFactorial is not defined
Encapsulation
Tận dụng private scope của self-invoking function, ta có thể tạo ra sự đóng gói dữ liệu (Encapsulation) của Object Oriented Programming.
Chẳng hạn ta có một đối tượng garage
như sau:
const garage = {
cars: [],
add(car) {
this.cars.push(car)
},
edit(index, car) {
this.cars[index] = car
},
remove(index) {
this.cars.splice(index, 1)
}
}
Sử dụng self-invoking function để tạo ra một object có chứa các property và method như sau:
const garage = (function() {
const cars = []
return {
getCar(id) {
return cars[id]
},
add(car) {
cars.push(car)
},
edit(index, car) {
cars[index] = car
},
remove(index) {
cars.splice(index, 1)
}
}
})()
Thuộc tính cars
của đối tượng garage
sẽ được ẩn giấu khỏi thế giới bên ngoài và chỉ có thể truy cập thông qua các phương thức đã định nghĩa.
Ví dụ:
garage.add({ name: 'BMW' })
garage.add({ name: 'Toyota' })
console.log(garage.cars) // undefined
console.log(garage.getCar(1)) // {name: "Toyota"}
Đây đồng thời cũng là một ứng dụng của Closure.
Summary
Điểm mạnh
- Tính đóng gói, các thành phần bên trong self-invoking không thể được truy cập từ bên ngoài.
- Cho phép xây dựng các factory function (là hàm tạo ra và trả về một object).
Điểm yếu
- Không có tính gọi lại.
Related
list
from [[Self-Invoking Functions]]
sort file.ctime asc
Footnotes
-
xem thêm JS Functions. ↩