Biểu thức của hàm

Biểu thức hàm là các hàm được tạo ở nơi dự kiến có một biểu thức. Bạn sẽ thường xuyên gặp biểu thức hàm dưới dạng giá trị được gán cho một biến. Mặc dù việc khai báo hàm luôn yêu cầu tên, nhưng bạn có thể sử dụng biểu thức hàm để tạo hàm ẩn danh bằng cách bỏ qua giá trị nhận dạng và theo sau từ khoá function với một cặp dấu ngoặc đơn chứa các tham số không bắt buộc:

const myVariable = function() { };

Sau đó, bạn có thể gọi các biểu thức hàm đó bằng cách sử dụng giá trị nhận dạng của biến:

const myVariable = function() {
    console.log( "This is my function." );
};

myVariable();
> "This is my function."

Bạn cũng có thể dùng biểu thức hàm để tạo hàm được đặt tên bằng cú pháp tương tự như phần khai báo hàm:

const myVariable = function myFunction() {
    console.log( "This is my function." );
};

myVariable();
> "This is my function."

Tuy nhiên, không giống như các nội dung khai báo hàm, biểu thức hàm được đặt tên chỉ có thể truy cập theo tên hàm trong chính hàm đó:

const myVariable = function myFunction() {
  console.log( `I'm a ${ typeof myFunction }.`);
};

typeof myFunction;
> "undefined"

typeof myVariable;
> "function"

myVariable();
> "I'm a function."

Tên liên kết với biểu thức hàm chủ yếu hữu ích khi gỡ lỗi. Biểu thức hàm được đặt tên cũng có thể tự gọi theo quy tắc đệ quy, mặc dù đây không phải là trường hợp sử dụng rất phổ biến trong quá trình phát triển hiện đại:

const myVariable = function myFunction() {
    console.log( "One second elapsed." );
    setTimeout( myFunction, 1000 );
};

setTimeout( myVariable, 1000 );
> "One second elapsed."
> "One second elapsed."
> "One second elapsed."
…

Biểu thức của hàm mũi tên

Biểu thức hàm mũi tên (thường được gọi là "hàm mũi tên" hoặc hiếm khi là "hàm lambda") được ra mắt trong ES6 để cung cấp cú pháp ngắn gọn nhằm tạo biểu thức hàm ẩn danh có một số hành vi riêng biệt.

Bạn có thể tạo hàm mũi tên ở bất cứ nơi nào biểu thức được mong đợi, ví dụ: dưới dạng giá trị được gán cho biến. Ở dạng phổ biến nhất, hàm mũi tên được tạo thành từ một cặp dấu ngoặc đơn phù hợp có chứa không hoặc nhiều tham số, một mũi tên được tạo thành từ một dấu bằng và ký tự lớn hơn (=>), và một cặp dấu ngoặc nhọn phù hợp chứa phần thân hàm:

const myFunction = () => {};

Trong một số điều kiện nhất định, bạn có thể làm cho cú pháp nhỏ gọn hơn nữa. Nếu chỉ sử dụng một tham số, bạn có thể bỏ dấu ngoặc đơn bắt đầu:

const myFunction = myParameter => {};

Khi muốn nội dung hàm trả về giá trị của một biểu thức, bạn không cần đặt nội dung hàm trong dấu ngoặc nhọn cũng như không phải từ khoá return:

const myFunction = () => 2 + 2

myFunction()
> 4

Các hàm mũi tên khác biệt ở chỗ không có ngữ cảnh riêng cho các giá trị arguments hoặc this. Thay vào đó, chúng kế thừa cả hai giá trị từ môi trường đóng gói theo từ vựng của hàm mũi tên (hàm đóng gần nhất cung cấp những ngữ cảnh đó).

function myParentFunction() {
    this.myProperty = true;
    let myFunction = () => {
            console.log( this );
    }
    myFunction();
};

let myInstance = new myParentFunction();
> Object { myProperty: true }

Hàm mũi tên gọi

Các hàm mũi tên không liên kết các đối số theo cách giống như các loại hàm khác. Đối tượng arguments trong phần nội dung của hàm mũi tên kế thừa giá trị từ môi trường đóng gói theo từ vựng gần nhất của hàm mũi tên đó:

function myFunction() {
    let myArrowFunction = () => {
            console.log( arguments[ 0 ] );
    }
    myArrowFunction( true );
};

myFunction( false );
> false

Trong ví dụ này, một hàm bên ngoài được gọi với đối số false sẽ gọi một hàm mũi tên bên trong với đối số true. Vì đối tượng arguments bên trong hàm mũi tên sẽ phân giải thành liên kết trong hàm bên ngoài, nên hàm bên trong sẽ ghi lại false của hàm bên ngoài.

Nếu không có đối tượng arguments để kế thừa từ ngữ cảnh mẹ, thì đối tượng arguments của hàm mũi tên sẽ không được xác định và việc cố gắng truy cập vào đối tượng đó sẽ gây ra lỗi:

let myArrowFunction = () => {
    console.log(arguments);
};
myArrowFunction( true );
> Uncaught ReferenceError: arguments is not defined

Biểu thức hàm được gọi ngay lập tức (IIFE)

Biểu thức hàm được gọi ngay (IIFE), đôi khi còn được gọi là "hàm ẩn danh tự thực thi", là một biểu thức hàm được gọi ngay khi được xác định. IIFE sử dụng biểu thức hàm được tạo bằng cách đóng gói hàm trong một toán tử nhóm. Sau đó, một cặp dấu ngoặc đơn phù hợp thứ hai sẽ gọi hàm, ngay sau chính định nghĩa hàm hoặc ngay sau toán tử nhóm. Nếu bạn sử dụng hàm chuẩn, sẽ không có sự khác biệt thực tế giữa 2 phương pháp:

(function() {
    console.log( "IIFE.")
    }
)();
> "IIFE."

(function() {
    console.log( "IIFE.")
    }
());
> "IIFE."

Ví dụ đầu tiên gọi biểu thức hàm được nhóm. Ví dụ thứ hai gọi một nội dung khai báo hàm bên trong các toán tử nhóm, kết quả cuối cùng sau đó được đánh giá dưới dạng một biểu thức được nhóm. Kết quả sẽ giống nhau trong cả hai trường hợp.

Tuy nhiên, có một điểm khác biệt khi IIFE của bạn là hàm mũi tên. Trong trường hợp này, dấu ngoặc đơn dùng để gọi hàm phải nằm ngoài toán tử nhóm, vì bản thân hàm mũi tên không phải là một biểu thức mà phải được tạo trong ngữ cảnh dự kiến có một biểu thức. Việc cố gắng gọi hàm mũi tên từ bên trong phạm vi của toán tử nhóm có nghĩa là bạn phải gọi một hàm mũi tên chưa được tạo trong ngữ cảnh của một biểu thức:

( () => {
    console.log( "IIFE." );
}() );
> Uncaught SyntaxError: missing ) in parenthetical

Vì các toán tử nhóm yêu cầu một biểu thức, nên hàm mũi tên trong các toán tử đó được xác định, cho phép dấu ngoặc đơn theo sau gọi biểu thức được nhóm:

( () => {
    console.log( "IIFE." );
} )();
> "IIFE."

Các ứng dụng cũ, thường dùng IIFE để quản lý phạm vi, cụ thể là để tránh gây ô nhiễm phạm vi toàn cục bằng các biến trong phạm vi hàmnội dung khai báo hàm. Trước khi được giới thiệu tính năng phạm vi khối trong ES6, bạn nên gói toàn bộ tập lệnh trong IIFE để ngăn chặn tình trạng vô tình ô nhiễm phạm vi toàn cầu.

Kiểm tra kiến thức

Bạn có thể gọi một biểu thức hàm được đặt tên theo tên bên ngoài hàm không?

Không