Phương thức của object trong JavaScript

Javascript căn bản | by Học Javascript

Trong JavaScript, Object là một trong những kiểu dữ liệu quan trọng nhất, cho phép lưu trữ và quản lý dữ liệu dưới dạng cặp key-value. Để làm việc với Object hiệu quả, phương thức (method) đóng vai trò quan trọng trong việc xử lý và thao tác với dữ liệu bên trong Object. Phương thức trong Object thực chất là các hàm được định nghĩa bên trong một Object và có thể được gọi để thực hiện các hành động cụ thể. Chúng giúp đóng gói logic, tổ chức mã nguồn gọn gàng hơn, và tạo ra các đối tượng có thể tương tác linh hoạt trong lập trình.

Trong bài viết này, mình sẽ tìm hiểu chi tiết về cách khai báo, gọi phương thức, sử dụng từ khóa this, cũng như các phương thức hữu ích khác của Object trong JavaScript.

Khái niệm về phương thức Object trong JavaScript

Định nghĩa phương thức (Method) trong Object

Trong JavaScript, phương thức (method) là một thuộc tính của Object, nhưng thay vì chứa dữ liệu như một biến thông thường, nó có giá trị là một hàm. Phương thức giúp Object thực hiện các hành động cụ thể, tương tác với dữ liệu bên trong Object, và cung cấp các chức năng hữu ích.

Cách khai báo phương thức trong Object

Có hai cách chính để khai báo một phương thức trong Object:

Cách 1: Khai báo phương thức bằng function expression

Ta có thể khai báo phương thức bằng cách gán một hàm ẩn danh (anonymous function) hoặc một hàm có tên (named function) vào một thuộc tính của Object.

let person = {
    name: "Alice",
    age: 25,
    greet: function() {
        console.log("Xin chào! Tôi là " + this.name);
    }
};

// Gọi phương thức
person.greet(); // Output: Xin chào! Tôi là Alice

Ở ví dụ trên:

  • greet là một phương thức của Object person.
  • Phương thức greet() khi được gọi sẽ in ra chuỗi chào có chứa tên của đối tượng.
  • Từ khóa this được sử dụng để tham chiếu đến thuộc tính name của Object hiện tại.

Cách 2: Khai báo phương thức theo cú pháp viết tắt (Method Shorthand - ES6)

Trong ES6, JavaScript hỗ trợ cách viết gọn hơn khi khai báo phương thức trong Object:

let person = {
    name: "Alice",
    age: 25,
    greet() {  // Viết gọn không cần dùng `function`
        console.log(`Xin chào! Tôi là ${this.name}`);
    }
};

person.greet(); // Output: Xin chào! Tôi là Alice

Cách này giúp mã nguồn ngắn gọn, dễ đọc, và khuyến khích sử dụng trong ES6+.

Sự khác biệt giữa phương thức và thuộc tính thông thường

Thuộc tính Phương thức
Lưu trữ một giá trị (số, chuỗi, boolean, mảng, object, v.v.). Lưu trữ một hàm thực hiện hành động nào đó.
Truy xuất dữ liệu đơn giản. Xử lý dữ liệu động, có thể thay đổi theo logic.
Ví dụ: person.name = "Alice" Ví dụ: person.greet()

Ví dụ minh họa:

let car = {
    brand: "Toyota",  // Thuộc tính lưu trữ dữ liệu
    speed: 100,
    
    accelerate() {  // Phương thức thực hiện hành động
        this.speed += 10;
        console.log(`Tốc độ hiện tại: ${this.speed} km/h`);
    }
};

console.log(car.brand); // Toyota (Thuộc tính)
car.accelerate(); // Tốc độ hiện tại: 110 km/h (Phương thức)

Cách khai báo và gọi phương phức Object trong JavaScript

Khai báo phương thức trong Object

Có hai cách phổ biến để khai báo phương thức trong một Object:

Cách 1: Khai báo phương thức thông qua function (Cách truyền thống)

Trong cách này, ta gán một hàm ẩn danh (anonymous function) hoặc hàm có tên (named function) làm giá trị của một thuộc tính trong Object.

Ví dụ:

let person = {
    name: "Alice",
    age: 25,
    greet: function() {
        console.log("Xin chào! Tôi là " + this.name);
    }
};

// Gọi phương thức
person.greet(); // Output: Xin chào! Tôi là Alice

Giải thích:

  • greet là một phương thức của Object person.
  • this.name dùng để truy cập thuộc tính name bên trong Object.

Cách 2: Cách viết tắt phương thức trong ES6+ (Method Shorthand)

Trong ES6, ta có thể khai báo phương thức theo cú pháp rút gọn, giúp mã nguồn ngắn gọn hơn.

let user = {
    name: "Bob",
    sayHello() {
        console.log(`Xin chào! Tôi là ${this.name}`);
    }
};

// Gọi phương thức bằng dấu `[]`
user["sayHello"](); // Output: Xin chào! Tôi là Bob

Khi nào nên dùng [] thay vì .?

Khi tên phương thức được lưu trong một biến động hoặc được tạo động.

let methodName = "sayHello";

let user = {
    name: "Bob",
    sayHello() {
        console.log(`Xin chào! Tôi là ${this.name}`);
    }
};

user[methodName](); // Output: Xin chào! Tôi là Bob

Ghi nhớ:

  • Dấu . phù hợp khi biết chắc tên phương thức.
  • Dấu [] hữu ích khi tên phương thức cần được truy xuất động.
Cách khai báo phương thức Ví dụ
Dùng function truyền thống greet: function() { console.log("Hello"); }
Dùng ES6+ (Shorthand) greet() { console.log("Hello"); }
Cách gọi phương thức Ví dụ
Dấu . (dot notation) object.method();
Dấu [] (bracket notation) object["method"]();

this trong phương thức của Object trong JavaScript

Định nghĩa this trong Object

Trong JavaScript, this là một từ khóa đặc biệt dùng để tham chiếu đến ngữ cảnh (context) hiện tại mà nó đang được sử dụng. Khi sử dụng this bên trong một phương thức của Object, nó thường trỏ đến chính Object đó.

Ví dụ đơn giản về this trong Object:

let person = {
    name: "Alice",
    age: 25,
    greet() {
        console.log(`Xin chào! Tôi là ${this.name}`);
    }
};

person.greet(); // Output: Xin chào! Tôi là Alice

Giải thích:

  • Trong phương thức greet(), this.name trỏ đến name của Object person.
  • Khi gọi person.greet(), this trỏ đến person, vì phương thức này được gọi trên Object đó.

this trỏ đến Object chứa nó

Trong JavaScript, giá trị của this phụ thuộc vào cách phương thức được gọi.

Các trường hợp thường gặp:

Trường hợp 1: this trỏ đến Object chứa phương thức

let car = {
    brand: "Toyota",
    showBrand() {
        console.log(`Hãng xe: ${this.brand}`);
    }
};

car.showBrand(); // Output: Hãng xe: Toyota

Giải thích: this.brand trỏ đến brand trong car, vì showBrand() được gọi từ car.

Trường hợp 2: this trong một hàm độc lập bên trong phương thức của Object

let person = {
    name: "Alice",
    age: 25,
    showInfo() {
        function innerFunction() {
            console.log(this.name); // Lỗi: this không trỏ đến person
        }
        innerFunction();
    }
};

person.showInfo(); // Output: undefined (hoặc lỗi trong chế độ nghiêm ngặt)

Giải thích:

  • innerFunction() không phải là phương thức của person, nó chỉ là một hàm thông thường được gọi bên trong showInfo().
  • Trong chế độ thông thường (non-strict mode), this trong innerFunction() sẽ trỏ đến window (hoặc global object trong Node.js).
  • Trong chế độ nghiêm ngặt (strict mode), this sẽ là undefined.

Trường hợp 3: Sử dụng this trong setTimeout()

let user = {
    name: "Bob",
    greet() {
        setTimeout(function () {
            console.log(`Xin chào! Tôi là ${this.name}`);
        }, 1000);
    }
};

user.greet(); // Output: undefined (hoặc lỗi)

Giải thích:

  • setTimeout() là một hàm toàn cục, vì vậy this trong đó trỏ đến window (hoặc global object trong Node.js), không phải user.
  • Do đó, this.name sẽ là undefined.

Cách khắc phục: Sử dụng arrow function để giữ giá trị của this.

let user = {
    name: "Bob",
    greet() {
        setTimeout(() => {
            console.log(`Xin chào! Tôi là ${this.name}`);
        }, 1000);
    }
};

user.greet(); // Output: Xin chào! Tôi là Bob

Arrow function không có this riêng, nó kế thừa this từ phạm vi chứa nó (user trong trường hợp này).

Những lỗi thường gặp khi sử dụng this trong phương thức Object

Lỗi 1: Mất ngữ cảnh this khi gán phương thức vào một biến

let car = {
    brand: "Toyota",
    showBrand() {
        console.log(this.brand);
    }
};

let getBrand = car.showBrand;
getBrand(); // Output: undefined (hoặc lỗi)

Nguyên nhân:

  • getBrand chỉ lưu tham chiếu đến hàm, không lưu thông tin về Object car.
  • Khi gọi getBrand(), this không còn trỏ đến car, mà trỏ đến window (global object).

Cách khắc phục: Dùng bind() để cố định this.

let getBrand = car.showBrand.bind(car);
getBrand(); // Output: Toyota

Lỗi 2: this bị thay đổi trong callback function

let person = {
    name: "Alice",
    greet() {
        setTimeout(function () {
            console.log(this.name);
        }, 1000);
    }
};

person.greet(); // Output: undefined

Cách khắc phục: Dùng arrow function để giữ nguyên giá trị của this.

let person = {
    name: "Alice",
    greet() {
        setTimeout(() => {
            console.log(this.name);
        }, 1000);
    }
};

person.greet(); // Output: Alice

Lỗi 3: this bị thay đổi khi sử dụng phương thức trong một sự kiện

let button = document.querySelector("button");

let user = {
    name: "Charlie",
    showName() {
        button.addEventListener("click", function () {
            console.log(this.name); // undefined
        });
    }
};

user.showName();

Nguyên nhân:

  • Trong trình xử lý sự kiện (event handler), this trỏ đến phần tử HTML (button), không phải user.

Cách khắc phục: Dùng arrow function hoặc bind().

let user = {
    name: "Charlie",
    showName() {
        button.addEventListener("click", () => {
            console.log(this.name);
        });
    }
};

user.showName(); // Output: Charlie

Arrow function không có this riêng, nên nó giữ this từ phạm vi cha (user).

Kiến thức quan trọng về this trong phương thức Object

Quy tắc chung của this trong Object:

Trường hợp Giá trị của this
Gọi phương thức trực tiếp từ Object this trỏ đến Object đó
Gọi hàm độc lập bên trong phương thức this trỏ đến window (hoặc undefined trong strict mode)
Gán phương thức vào biến rồi gọi this trỏ đến window
setTimeout() hoặc setInterval() this trỏ đến window
Dùng arrow function trong Object this kế thừa từ phạm vi cha

Các phương thức hữu ích trong Object JavaScript

JavaScript cung cấp nhiều phương thức hữu ích để thao tác với Object. Dưới đây là một số phương thức quan trọng giúp bạn sao chép, truy xuất và kiểm tra thuộc tính của Object một cách hiệu quả.

Object.assign() – Sao chép thuộc tính từ Object này sang Object khác

Phương thức Object.assign(target, ...sources) dùng để sao chép các thuộc tính của một hoặc nhiều Object nguồn vào Object đích.

Cú pháp:

Object.assign(target, source1, source2, ...);
  • target: Object đích (có thể là Object rỗng {} hoặc một Object có sẵn).
  • source1, source2, ...: Các Object nguồn có thuộc tính cần sao chép.
let obj1 = { a: 1, b: 2 };
let obj2 = { b: 3, c: 4 };

let mergedObj = Object.assign({}, obj1, obj2);
console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }

Lưu ý:

  • Thuộc tính trùng nhau sẽ bị ghi đè bởi Object sau cùng (b: 2 bị ghi đè bởi b: 3).
  • Object.assign() chỉ thực hiện sao chép nông (shallow copy), nghĩa là nếu một thuộc tính chứa Object con, nó vẫn tham chiếu đến Object cũ.

Sao chép nông và vấn đề tham chiếu:

let obj1 = { a: 1, b: { x: 10 } };
let obj2 = Object.assign({}, obj1);

obj2.b.x = 99;
console.log(obj1.b.x); // Output: 99 (vì tham chiếu đến cùng một Object con)

Giải pháp: Dùng structuredClone() hoặc thư viện Lodash.cloneDeep() để sao chép sâu.

Object.keys() – Lấy danh sách các key của Object

Phương thức Object.keys(obj) trả về một mảng chứa tất cả các key (tên thuộc tính) của Object.

Object.keys(object);

Ví dụ:

let person = { name: "Alice", age: 25, city: "Hanoi" };

console.log(Object.keys(person)); 
// Output: ["name", "age", "city"]

Ứng dụng:

  • Dùng để lặp qua Object bằng forEach() hoặc map().
  • Kiểm tra xem Object có rỗng không bằng Object.keys(obj).length === 0.

Kiểm tra Object có rỗng không

let obj = {};
console.log(Object.keys(obj).length === 0); // Output: true

Object.values() – Lấy danh sách các value của Object

Phương thức Object.values(obj) trả về một mảng chứa các giá trị của thuộc tính trong Object.

Cú pháp:

Object.values(object);

Ví dụ:

let person = { name: "Alice", age: 25, city: "Hanoi" };

console.log(Object.values(person)); 
// Output: ["Alice", 25, "Hanoi"]

Ứng dụng:

  • Duyệt qua các giá trị của Object mà không cần biết key.
  • Tổng hợp dữ liệu từ Object.

Object.entries() – Chuyển Object thành mảng các cặp [key, value]

Phương thức Object.entries(obj) trả về một mảng chứa các cặp [key, value], mỗi phần tử là một mảng con có 2 phần tử: key và value.

Object.entries(object);

Ví dụ:

let person = { name: "Alice", age: 25, city: "Hanoi" };

console.log(Object.entries(person)); 
// Output: [["name", "Alice"], ["age", 25], ["city", "Hanoi"]]

Ứng dụng:

Dùng forEach() để lặp qua các cặp [key, value].

Object.entries(person).forEach(([key, value]) => {
    console.log(`${key}: ${value}`);
});
// Output:
// name: Alice
// age: 25
// city: Hanoi
  • Chuyển đổi Object thành Map (new Map(Object.entries(obj))).

Object.hasOwnProperty() – Kiểm tra Object có thuộc tính cụ thể không

Phương thức Object.hasOwnProperty(prop) kiểm tra xem một Object có chứa một thuộc tính cụ thể hay không.

object.hasOwnProperty(property);

Ví dụ:

let person = { name: "Alice", age: 25 };

console.log(person.hasOwnProperty("name"));  // Output: true
console.log(person.hasOwnProperty("city"));  // Output: false

Ứng dụng:

  • Kiểm tra một thuộc tính có tồn tại trong Object hay không trước khi truy xuất nó.
  • Tránh lỗi khi truy xuất thuộc tính không tồn tại.

Lưu ý:

  • hasOwnProperty() chỉ kiểm tra các thuộc tính riêng của Object, không kiểm tra thuộc tính kế thừa từ prototype.

Phương thức Getter và Setter trong Object JavaScript

JavaScript cung cấp gettersetter để giúp truy xuất và cập nhật dữ liệu trong Object một cách linh hoạt và bảo mật hơn.

Định nghĩa Getter và Setter trong Object

  • Getter (get): Là một phương thức trong Object dùng để lấy giá trị của thuộc tính, nhưng hoạt động như một thuộc tính thông thường khi truy xuất.
  • Setter (set): Là một phương thức trong Object dùng để cập nhật giá trị của thuộc tính, có thể bao gồm các quy tắc kiểm tra dữ liệu đầu vào.

Tóm gọn:

  • Getter giúp lấy giá trị mà không cần gọi như hàm.
  • Setter giúp thay đổi giá trị và có thể kèm theo kiểm tra hợp lệ.

Cách sử dụng get để truy xuất dữ liệu từ Object

Cú pháp của get:

let object = {
    get propertyName() {
        return value;
    }
};
  • Khi gọi object.propertyName, phương thức get sẽ được thực thi như một thuộc tính.

Ví dụ:

let person = {
    firstName: "Alice",
    lastName: "Nguyen",
    
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    }
};

console.log(person.fullName); 
// Output: Alice Nguyen

Điểm nổi bật:

  • Không cần gọi fullName() như một hàm.
  • get giúp tạo thuộc tính động mà không cần lưu sẵn giá trị.

Cách sử dụng set để thay đổi dữ liệu trong Object

Cú pháp của set:

let object = {
    set propertyName(value) {
        // Xử lý khi gán giá trị
    }
};
  • Khi gán object.propertyName = newValue, phương thức set sẽ được kích hoạt.
let person = {
    firstName: "Alice",
    lastName: "Nguyen",
    
    set fullName(name) {
        let parts = name.split(" ");
        this.firstName = parts[0];
        this.lastName = parts[1];
    }
};

person.fullName = "Bob Tran";
console.log(person.firstName); // Output: Bob
console.log(person.lastName);  // Output: Tran

Lợi ích của set:

  • Có thể thực hiện xử lý dữ liệu trước khi gán (ví dụ: tách họ và tên).
  • Giúp bảo vệ dữ liệu bằng cách kiểm tra giá trị đầu vào.

Ví dụ minh họa Getter và Setter

Ví dụ 1: Kiểm tra dữ liệu đầu vào với set

let user = {
    _age: 0, // Thuộc tính ẩn (quy ước bắt đầu bằng "_")
    
    get age() {
        return this._age;
    },
    
    set age(value) {
        if (value < 0) {
            console.log("Tuổi không hợp lệ!");
        } else {
            this._age = value;
        }
    }
};

user.age = 25;  
console.log(user.age); // Output: 25

user.age = -5;  // Output: Tuổi không hợp lệ!

Điểm nổi bật:

  • Thuộc tính _age chỉ có thể được thay đổi qua set age().
  • Ngăn chặn gán giá trị không hợp lệ (tuổi không thể âm).

Ví dụ 2: Tự động tính toán giá trị với get

let rectangle = {
    width: 10,
    height: 5,

    get area() {
        return this.width * this.height;
    }
};

console.log(rectangle.area); // Output: 50

Ứng dụng thực tế:

  • Dùng get để tính toán giá trị động mà không cần tạo biến trung gian.

Ví dụ 3: Kết hợp get và set trong Class

Getter và Setter cũng hoạt động trong Class.

class Product {
    constructor(name, price) {
        this.name = name;
        this._price = price; // Biến private
    }

    get price() {
        return `${this._price} USD`;
    }

    set price(value) {
        if (value < 0) {
            console.log("Giá sản phẩm không hợp lệ!");
        } else {
            this._price = value;
        }
    }
}

let laptop = new Product("MacBook", 1500);
console.log(laptop.price); // Output: 1500 USD

laptop.price = -100;  // Output: Giá sản phẩm không hợp lệ!
laptop.price = 1200;
console.log(laptop.price); // Output: 1200 USD

Kết quả 1:

Ứng dụng thực tế:

  • get price() đảm bảo giá được hiển thị kèm đơn vị tiền tệ.
  • set price(value) giúp kiểm tra giá không âm.

Kết bài

Phương thức trong Object là một phần quan trọng của JavaScript, giúp tổ chức và thao tác dữ liệu hiệu quả. Việc hiểu rõ cách khai báo, gọi phương thức cũng như cách xử lý các vấn đề liên quan đến this, lỗi thường gặp sẽ giúp lập trình viên viết mã nguồn sạch, dễ bảo trì và tối ưu hơn.

Ngoài ra, phương thức còn đóng vai trò quan trọng trong lập trình hướng đối tượng (OOP), giúp xây dựng các mô hình dữ liệu linh hoạt, dễ mở rộng. Nắm vững kiến thức về phương thức Object sẽ giúp bạn làm chủ JavaScript, từ xử lý dữ liệu đơn giản đến phát triển các ứng dụng phức tạp.

Bài viết liên quan

  • 2