JavaScript: Các trường hợp không nên sử dụng hàm mũi tên


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Tổng quan

Hàm mũi tên không có giá trị this và đối tượng arguments riêng. Do đó, bạn không nên sử dụng nó như một trình xử lý sự kiện (event handler), một phương thức của một đối tượng chữ viết tắt (object literal), một phương thức nguyên mẫu (prototype method) hoặc khi bạn có một hàm cần sử dụng đối tượng arguments.

1) Event handlers

Giả sử bạn có trường nhập dữ liệu đầu vào sau:

<input type="text" name="username" id="username" placeholder="Enter a username">

Và bạn muốn hiển thị thông báo chào mừng khi người dùng nhập tên người dùng của họ. Phần sau đây cho thấy phần tử <div> sẽ hiển thị thông báo chào mừng:

<div id="greeting"></div>

Khi người dùng nhập tên người dùng của họ, bạn nắm bắt giá trị hiện tại của đầu vào và cập nhật nó vào phần tử <div>:

const greeting = document.querySelector('#greeting');
const username = document.querySelector('#username');
username.addEventListener('keyup', () => {
  greeting.textContent = 'Hello ' + this.value;
});

Tuy nhiên, khi bạn thực thi mã, bạn sẽ nhận được thông báo sau bất kể bạn gõ gì:

Hello undefined

Điều đó có nghĩa là this.value trong trình xử lý sự kiện luôn trả về undefined.

Như đã đề cập trước đó, hàm mũi tên không có giá trị this riêng. Nó sử dụng giá trị this của lexical scope. Trong ví dụ trên, this trong hàm mũi tên tham chiếu đến đối tượng chung.

Trong trình duyệt web, đối tượng chung là window. Đối tượng window không có thuộc tính value. Do đó, JavaScript engine thêm thuộc tính value vào đối tượng window và đặt giá trị của nó thành undefined.

Để khắc phục sự cố này, thay vào đó bạn cần sử dụng một hàm thông thường. Giá trị this sẽ được liên kết với phần tử <input> kích hoạt sự kiện.

username.addEventListener('keyup', function () {
    input.textContent = 'Hello ' + this.value;
});

2) Object methods

Xem đối tượng counter sau :

const counter = {
  count: 0,
  next: () => ++this.count,
  current: () => this.count
};

Đối tượng counter có hai phương thức: current() và next(). Phương thức current() trả về giá trị bộ đếm hiện tại và phương thức next() trả về giá trị bộ đếm tiếp theo.

Phần sau đây hiển thị giá trị bộ đếm tiếp theo sẽ là 1:

console.log(counter.next());

Tuy nhiên, nó trả về NaN.

Lý do là khi bạn sử dụng hàm mũi tên bên trong đối tượng, nó sẽ kế thừa giá trị this từ lexical scope, là phạm vi toàn cục trong ví dụ này.

this.count bên trong phương thức next() tương đương với window.count (trong trình duyệt web).

Theo mặc định window.count là undefined vì đối tượng window không có thuộc tính count. Phương thức next() cộng thêm một vào giá trị undefined đó và kết quả là NaN.

Để khắc phục điều này, bạn sử dụng các hàm thông thường làm phương thức của một object literal như sau:

const counter = {
    count: 0,
    next() {
        return ++this.count;
    },
    current() {
        return this.count;
    }
};

Bây giờ, việc gọi phương thức next() sẽ trả về một kết quả như mong đợi:

console.log(counter.next()); // 1

3) Prototype methods

Xem đối tượng Counter sau sử dụng mẫu prototype

function Counter() {
    this.count = 0;
}

Counter.prototype.next = () => {
    return this.count;
};

Counter.prototype.current = () => {
    return ++this.next;
}

Giá trị  this trong các phương thức next() và current() tham chiếu đến đối tượng toàn cục. Vì bạn muốn giá trị this bên trong các phương thức tham chiếu đến đối tượng Counter, nên thay vào đó bạn cần sử dụng các hàm thông thường:

function Counter() {
    this.count = 0;
}

Counter.prototype.next = function () {
    return this.count;
};

Counter.prototype.current = function () {
    return ++this.next;

4) Các hàm sử dụng đối tượng arguments

Hàm mũi tên không có đối tượng arguments. Vì vậy, nếu bạn có hàm sử dụng đối tượng arguments thì bạn không thể sử dụng hàm mũi tên.

Ví dụ: hàm concat() sau sẽ không hoạt động:

const concat = (separator) => {
    let args = Array.prototype.slice.call(arguments, 1);
    return args.join(separator);
}

Thay vào đó, bạn sử dụng một hàm thông thường như sau:

function concat(separator) {
    let args = Array.prototype.slice.call(arguments, 1);
    return args.join(separator);
}

Tóm tắt

  • Hàm mũi tên không có giá trị this riêng. Thay vào đó, nó sử dụng giá trị this của lexical scope. Hàm mũi tên cũng không có đối tượng arguments.
  • Tránh sử dụng hàm mũi tên cho các trình xử lý sự kiện, phương thức đối tượng, phương thức nguyên mẫu và các hàm sử dụng đối tượng arguments.
« Trước: Tìm hiểu các đối tượng Map và Set trong JavaScript
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!