Trong bài viết trước “Lập trình hướng đối tượng với JavaScript”, chúng ta đã so sánh ngôn ngữ lập trình hướng đối tượng với ngôn ngữ lập trình đối tượng. Trong bài viết này, chúng ta sẽ cùng tìm hiểu sâu hơn về các khía cạnh liên quan đến lập trình hướng đối tượng của ngôn ngữ lập trình JavaScript.
Contents
Lịch sử của đối tượng
Có một câu nói mang đầy tính triết học như sau:
Bạn đang xem: Lập trình hướng đối tượng cùng JavaScript
“Một vấn đề phức tạp có thể được quản lý bằng cách chia nó thành những phần nhỏ độc lập với nhau.”
Ở đây, mỗi phần nhỏ chính là một đối tượng. Đối tượng cung cấp cho chúng ta các phương thức để gọi mà không cần biết chi tiết bên trong nó. Khi chúng ta xây dựng các đối tượng hoạt động theo yêu cầu, chúng ta có thể sử dụng chúng để giải quyết những bài toán lớn một cách đơn giản. Đây chính là tư tưởng “chia để trị”.
Phương thức (Method)
Phương thức là một thành phần quan trọng trong đối tượng. Trong C++ hoặc Java, chúng ta chỉ có thể sử dụng các phương thức public. Chúng ta sử dụng các phương thức này để lấy giá trị hoặc thay đổi thông tin của các thuộc tính trong đối tượng. Trong JavaScript, một phương thức chính là một thuộc tính với giá trị là một hàm. Dưới đây là một ví dụ về một phương thức đơn giản:
var rabbit = {};
rabbit.speak = function(line) {
console.log("Con thỏ nói '" + line + "'");
};
rabbit.speak("Tôi còn sống."); // => Con thỏ nói 'Tôi còn sống.'
Thông thường, một phương thức sẽ thực hiện một số thao tác trên đối tượng gọi nó. Để biểu thị đối tượng đã gọi phương thức, JavaScript cung cấp từ khóa this
để chúng ta sử dụng bên trong phương thức. Ví dụ trên có thể thay đổi như sau:
function speak(line) {
console.log("Con thỏ " + this.type + " nói '" + line + "'");
}
var whiteRabbit = {type: "trắng", speak: speak};
var fatRabbit = {type: "béo", speak: speak};
whiteRabbit.speak("Tôi còn sống."); // => Con thỏ trắng nói 'Tôi còn sống.'
fatRabbit.speak("Tôi còn sống."); // => Con thỏ béo nói 'Tôi còn sống.'
Ý tưởng sử dụng từ khóa this
này cũng được áp dụng trong C++ hoặc Java.
Sự tương quan với bind, call, apply
Nếu bạn chưa biết về bind
, call
, hay apply
, bạn có thể tìm hiểu về chúng tại đây. Khi gọi 3 hàm này, tham số đầu tiên chính là giá trị của con trỏ this
.
function speak(line) {
console.log("Con thỏ " + this.type + " nói '" + line + "'");
}
var whiteRabbit = {type: "trắng"};
var fatRabbit = {type: "béo"};
var sexyRabbit = {type: "gợi cảm"};
speak.apply(whiteRabbit, ["Tôi còn sống."]); // => Con thỏ trắng nói 'Tôi còn sống.'
speak.call(fatRabbit, "Tôi còn sống."); // => Con thỏ béo nói 'Tôi còn sống.'
var sexyRabbitSpeak = speak.bind(sexyRabbit, "Tôi còn sống.");
sexyRabbitSpeak(); // => Con thỏ gợi cảm nói 'Tôi còn sống.'
Prototype
Xem thêm : Cú pháp lập trình JavaScript (phần 1)
Prototype
là khái niệm đặc biệt trong JavaScript. Khác với C++ hoặc Java, một đối tượng JavaScript luôn có ít nhất một thuộc tính prototype bên trong nó, và prototype chính là một đối tượng. Khi một đối tượng gọi đến một thuộc tính mà nó không có, nó sẽ tìm trong prototype. Hãy xem ví dụ sau:
var empty = {};
console.log(empty.toString); // => function toString() { [native code] }
console.log(empty.toString()); // => [object Object]
Rõ ràng, tôi chỉ khai báo empty
là một đối tượng mà không định nghĩa thêm thuộc tính nào. Tuy nhiên, ví dụ trên cho thấy thuộc tính toString
tồn tại trong đối tượng empty
. Điều này là do toString
là một thuộc tính của prototype
, mà một object
thì luôn chứa thuộc tính prototype
.
var empty = {};
console.log(Object.getPrototypeOf(empty) == Object.prototype); // => true
console.log(Object.getPrototypeOf(Object.prototype)); // => null
Constructors (Hàm khởi tạo)
Nếu bạn đã biết về lập trình hướng đối tượng, bạn chắc chẳng xa lạ gì với constructor
. Trong JavaScript, hàm khởi tạo constructor sẽ chứa từ khóa this
để biểu thị đối tượng được tạo ra từ nó. Thông thường, constructor bắt đầu bằng chữ cái viết hoa để phân biệt với các hàm khác. Bạn sẽ sử dụng từ khóa new
đứng trước tên hàm để tạo ra một đối tượng mới từ constructor này. Dưới đây là một ví dụ đơn giản về constructor:
function Rabbit(type) {
this.type = type;
this.greeting = function(){
console.log(this.type + " thỏ" + " nói Xin chào!");
}
}
var blackRabbit = new Rabbit("đen");
console.log(blackRabbit.type); // => đen
blackRabbit.greeting(); // => đen thỏ nói Xin chào!
var killerRabbit = new Rabbit("giết người");
console.log(killerRabbit.type); // => giết người
killerRabbit.greeting(); // => giết người thỏ nói Xin chào!
Lúc này, mỗi đối tượng được tạo ra từ constructor Rabbit
sẽ có hai thuộc tính type
và greeting
. Tuy nhiên, bạn vẫn có thể thêm các thuộc tính khác thông qua Object.prototype
như sau:
function Rabbit(type) {
this.type = type;
this.greeting = function(){
console.log(this.type + " thỏ" + " nói Xin chào!");
}
}
Rabbit.prototype.sayGoodbye = function (){
console.log(this.type + " thỏ" + " nói Tạm biệt!");
}
var blackRabbit = new Rabbit("đen");
blackRabbit.sayGoodbye(); // => đen thỏ nói Tạm biệt!
Ghi đè thuộc tính
Trong ví dụ trên, Rabbit
chứa thuộc tính type
. Với mỗi đối tượng được tạo ra từ constructor Rabbit
, bạn có thể thay đổi giá trị thuộc tính của một đối tượng mà không ảnh hưởng tới các đối tượng khác.
function Rabbit(type) {
this.type = type;
this.greeting = function(){
console.log(this.type + " thỏ" + " nói Xin chào!");
}
}
Rabbit.prototype.teeth = "nhỏ";
var blackRabbit = new Rabbit("đen");
var killerRabbit = new Rabbit("giết người");
console.log(blackRabbit.teeth); // => nhỏ
console.log(killerRabbit.teeth); // => nhỏ
killerRabbit.teeth = "dài";
console.log(blackRabbit.teeth); // => nhỏ
console.log(killerRabbit.teeth); // => dài
Tính chất đặc trưng của lập trình hướng đối tượng trong JavaScript
Tính đóng gói (Encapsulation)
Tính đóng gói: che giấu dữ liệu, không cho phép truy cập dữ liệu trực tiếp từ bên ngoài, mà phải thông qua các method được cung cấp.
function Person(_name){
var name = _name;
this.setName = function(_name){
name = _name;
}
this.getName = function(){
return name;
}
}
var person = new Person("LamPham");
console.log(person.name); // => undefined
console.log(person.getName()); // => LamPham
person.setName("LP Devs");
console.log(person.getName()); // => LP Devs
Tính kế thừa (Inheritance)
Tính kế thừa: đối tượng con sẽ kế thừa những thuộc tính của đối tượng cha mà không cần phải định nghĩa lại. Mặc dù JavaScript không hỗ trợ tính kế thừa trực tiếp, nhưng ta vẫn có thể tuỳ biến để áp dụng tính chất này trong JavaScript.
function Person(_name){
var name = _name;
this.setName = function(_name){
name = _name;
}
this.getName = function(){
return name;
}
}
function Student(_name, _school){
var school = _school;
Person.call(this, _name);
this.setSchool = function(_school){
school = _school;
}
this.getSchool = function(){
return school;
}
}
var student = new Student("LamPham", "HUST");
console.log(student.getName()); // => LamPham
console.log(student.getSchool()); // => HUST
student.setSchool("NEU");
student.setName("Ronaldo");
console.log(student.getName()); // => Ronaldo
console.log(student.getSchool()); // => NEU
Ngoài ra, còn hai tính chất là tính đa hình và tính trừu tượng. Tuy nhiên, việc áp dụng hai tính chất này trong JavaScript là không rõ ràng. Do đó, tôi sẽ không trình bày về chúng nữa.
Kết luận
Đây là những khía cạnh cơ bản của lập trình hướng đối tượng áp dụng trong JavaScript. Tôi có thể tóm tắt như sau:
- Method: chúng ta sử dụng method để lấy giá trị và thay đổi giá trị thuộc tính của đối tượng.
- Prototype: mọi đối tượng đều chứa thuộc tính prototype. Chúng ta có thể thay đổi và thêm thuộc tính của đối tượng dựa vào prototype.
- Constructor: Có thể tạo đối tượng mới từ một hàm khởi tạo constructor sử dụng từ khóa
new
. - Tính đóng gói: che giấu dữ liệu; không cho phép truy cập dữ liệu trực tiếp từ bên ngoài, mà phải thông qua các phương thức được cung cấp.
- Tính kế thừa: đối tượng con sẽ kế thừa những thuộc tính của đối tượng cha mà không cần phải định nghĩa lại.
Xem thêm : Mức lương Lập Trình C/C++
Việc áp dụng lập trình hướng đối tượng trong JavaScript có thể khó khăn. Tuy nhiên, nếu bạn hiểu rõ những kiến thức cơ bản mà tôi đã trình bày ở trên, tôi tin chắc rằng bạn có thể dễ dàng tìm hiểu thêm và áp dụng lập trình hướng đối tượng trong JavaScript.
Thực hành
1. Vector
Xây dựng constructor Vector
để biểu diễn một vector trong không gian hai chiều, có hai tham số đầu vào là x
và y
. Xây dựng 2 methods cho Vector prototype
là plus
và minus
như ví dụ sau:
console.log(new Vector(1, 2).plus(new Vector(2, 3))); // => Vector{x: 3, y: 5}
console.log(new Vector(1, 2).minus(new Vector(2, 3))); // => Vector{x: -1, y: -1}
2. Interface
Định nghĩa hàm logFive
với đầu vào là một object
. Thực hiện ghi ra log 5 phần tử đầu tiên hoặc ít hơn nếu số phần tử thoả mãn ít hơn 5. Implement object kiểu ArraySeq
với đầu vào là một mảng và một object kiểu RangeSeq
với đầu vào là 2 số nguyên biểu diễn một khoảng.
logFive(new ArraySeq([1, 2])); // => 1 // => 2
logFive(new RangeSeq(100, 1000)); // => 100 // => 101 // => 102 // => 103 // => 104
Tham khảo code tại đây.
Xin chào và hẹn gặp lại ở bài viết sau trong series JavaScript cơ bản. Thân ái,
Tham khảo
- Blog Complete JavaScript
- Facebook Fanpage: Complete JavaScript
- Facebook Group: Hỏi đáp JavaScript VN
Nguồn: https://laptrinhc.edu.vn
Danh mục: Ngôn ngữ lập trình