Như đã chia sẻ ở bài viết trước, trong năm 2016 mình sẽ dành thời gian trau dồi kĩ năng front-end, do đó số lượng các bài viết về front-end trên blog sẽ nhiều hơn một chút. Sau series C# hay ho được nhiều người đón nhận, năm nay blog sẽ có thêm series Javascript sida. Lý do là: càng học sẽ càng thấy C# nó hay ho, trong khi đó càng học lại càng thấy Javascript nó sida, bạn nào không tin cứ theo dõi series sẽ biết.
Bạn nào theo dõi blog lâu cũng biết mình có một số tình cảm khá phức tạp cả yêu lẫn ghét dành cho javascript. Về bản thân ngôn ngữ, cá nhân mình thấy nó là một ngôn ngữ trời đánh, khá sida, làm bao nhiêu lần mình phải thốt lên đ.m hay WTF khi học.
Javascript vốn được thiết kế một cách tạm bợ thô sơ, dùng để validate ở client side (Bạn nào tò mò muốn biết thêm về lịch sử của js có thể đọc thêm cuốn Professional JavaScript for Web Developers). Song chẳng hiểu duyên trời đưa đẩy thế nào, JavaScript cùng với PHP lại trở thành hai ngôn ngữ được sử dụng ở khắp mọi nơi, dù hứng chịu biết bao gạch đá.
Tuy ghét thì ghét, đã theo nghiệp web developer thì trước sau gì cũng phải làm việc hằng ngày với js. Bản thân js tuy xấu, nhưng nó lại đi kèm vô số thư viện/framework cực kì hay vào hữu ích (jQuery, AngularJS, …), nhờ NodeJS nó còn lấn sân qua luôn cả back-end. Do đó học và thuần thục JS chẳng bao giờ thiệt cả.
Nhận ra vốn kiến thức về js của mình còn đôi chỗ lủng củng và thiếu sót, trong series Javascript sida mình sẽ viết bài chia sẻ về một số khái niệm từ đơn giản đến phức tạp trong js, vừa củng cố kiến thức của mình, vừa giúp ích cho các bạn. Ở bài đầu tiên, mình sẽ giới thiệu kiến thức cơ bản nhất trong JavaScript: object.
Object là khỉ gì?
Ai từng học qua môn Lập trình hướng đối tượng đều biết đến các khái niệm Class và Object (Có thể xem lại ở đây: http://www.qhonline.info/php-nang-cao/57/lap-trinh-huong-doi-tuong-co-ban-ve-nhung-khai-niem.html). Tuy nhiên, trong JavaScript không có Class mà chỉ có Object, do đó sẽ khiến một số bạn cảm thấy khó hiểu (Đấy, mình đã nói bao nhiêu lần là js nó sida rồi mà).
Trong các ngôn ngữ OOP như C++, Java, C#, … Có thể tạm hiểu Class chính là cái khung, còn Object là vật thể tạo ra dựa vào cái khung đó. Js không có class, ta có thể khởi tạo 1 object mà không cần xác định class của nó, có thể hiểu toàn bộ object đều có class chung là Object.
Ta có thể hiểu một object là một tập hợp các trường dữ liệu (property) và các hàm (method). Như ví dụ dưới đây, object Student có 2 trường là firstName và lastName, có hàm showName
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var person = { | |
firstName: 'Hoang', | |
lastName: 'Pham', | |
showName: function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
} | |
}; |
Khởi tạo object thế éo nào
Trong một số ngôn ngữ khác, để khởi tạo object, ta dùng từ khóa new + tên class. Tuy nhiên, do trong JavaScript không có khái niệm class, ta có thể tạo object theo 1 trong 2 cách sau. Cách khởi tạo object bằng Object Literal thường được sử dụng nhiều hơn.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Cách 1 : Object literal | |
// Khai báo toàn bộ các trường và hàm | |
var person = { | |
firstName: 'Hoang', | |
lastName: 'Pham', | |
showName: function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
} | |
}; | |
// Cách 2 : Object constructor | |
var psn = new Object(); | |
psn.firstName = 'Hoang'; | |
psn.lastName = 'Pham'; | |
psn.showName = function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
}; |
Với các ứng dụng đơn giản, ta có thể tạm dùng 2 cách này. Tuy nhiên, với một số bài toán phức tạp hơn, nếu dùng object literal mỗi lần khởi tạo object sẽ khiến code dài và trùng lặp (Lần nào cũng phải khai báo lại các property và method). Để giải quyết vấn đề này, người ta sử dụng một pattern gọi là Constructor pattern. Một function sẽ đóng vai trò constructor để khởi tạo object (Cách này na ná khai báo class trong các ngôn ngữ khác).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Person(firstName, lastName) { | |
this.firstName = firstName; | |
this.lastName = lastName; | |
this.showName = function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
}; | |
} | |
// Khi muốn tạo object person chỉ cần gọi constructor | |
var psn1 = new Person('Hoang', 'Pham'); | |
var psn2 = new Person('Hoang', 'Nguyen'); |
Một cách khác cũng hay được sử dụng đó là dùng prototype (Mình sẽ nói kĩ hơn về prototype trong những bài sau), nhưng mình thấy đa phần người ta sử dụng Constructor pattern nhiều hơn.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Person() {} | |
Person.prototype.firstName = 'Hoang'; | |
Person.prototype.lastName = 'Pham'; | |
Person.prototype.showName = function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
}; | |
// Object được tạo sẽ có sẵn các trường firstName, lastName | |
// và hàm showName | |
var psn1 = new Person(); | |
console.log(psn1.firstName); //Hoang | |
psn1.showName; //Hoang Pham |
Nghịch ngợm với object
1. Truy xuất một trường/hàm của object
Để truy xuất một trường/hàm của object, ta có thể dùng dấu . (dot notation) và dấu [] (bracket notation). Dot notation thường được sử dụng nhiều hơn, nhưng bracket notation có thể làm được nhiều trò hay hơn.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var person = { | |
firstName: 'Hoang', | |
lastName: 'Pham', | |
50: 'Hi', // Property có tên là số, không dùng dotNotation được | |
showName: function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
} | |
}; | |
console.log(person.firstName); // Hoang | |
console.log(person['firstName']); // Hoang | |
console.log(person.50); // Bị lỗi | |
console.log(person['50']); // Hi | |
console.log(person.showName()); // Hoang Pham | |
console.log(person['showName']()); // Hoang Pham |
Để duyệt qua toàn bộ các trường của một object, ta chỉ cần dùng hàm for đơn giản
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var person = { | |
firstName: 'Hoang', | |
lastName: 'Pham', | |
showName: function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
} | |
}; | |
for(var prop in person) { | |
console.log(prop); // firstName, lastName, showName | |
} |
2. Thêm/Xóa một trường/hàm của object
Với các ngôn ngữ static typing như C#, Java, một object được khởi tạo dựa trên class, do đó chúng luôn có các trường và hàm cố định. Tuy nhiên, do JavaScript là ngôn ngữ dynamic typing, ta có thể dễ dàng thêm/xóa các trường trong code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var person = { | |
firstName: 'Hoang', | |
lastName: 'Pham', | |
showName: function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
} | |
}; | |
delete person.lastName; // Xóa trường lastName | |
person.lName = 'Just adding'; // Thêm trường lName | |
console.log(person.lastName); // undefined | |
console.log(person.lName); // Just adding |
3. Serialize và deserialize
Để giao tiếp với server, JavaScript thường submit dữ liệu dưới dạng pair-value (thông qua form) hoặc JSON. Do đó, javascript hỗ trợ sẵn việc chuyển object sang chuỗi JSON và ngược lại
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var person = { | |
firstName: 'Hoang', | |
lastName: 'Pham', | |
showName: function() { | |
console.log(this.firstName + ' ' + this.lastName); | |
} | |
}; | |
// Serialize sẽ làm mất method, chỉ giữ các property | |
JSON.stringify(person); // '{"firstName":"Hoang","lastName":"Pham"}' | |
var jsonString = '{"firstName":"Hoang","lastName":"Pham"}'; | |
var psn = JSON.parse(jsonString); // Chuyển string thành object | |
console.log(psn.firstName); // Hoang | |
console.log(psn.lastName); // Pham |
Qua bài viết này, các bạn đã có cái nhìn tổng quát về object trong JavaScript. Do sự sida của nó, việc hiện thực các đặc tính của OOP như inheritance, encapsulation, polymophism trong js khá rắc rối và phức tạp. Các bạn hãy đón xem trong bài viết sau về OOP trong JavaScript nhé.
Bạn nào từng học Java có thể xem thử bài này: https://codeaholicguy.wordpress.com/2015/12/22/javascript-object-duoi-con-mat-cua-java-developer/. Bài viết có tham khảo từ nguồn tiếng Anh tại: http://javascriptissexy.com/javascript-objects-in-detail/.
mình vẫn chưa thấy nó sida chỗ nào 😥
LikeLike
Nó ko có class mà lại có object đó bạn, chẳng sida là gì :v
LikeLiked by 1 person
Bài viết hay quá bác, lót dép hóng phần tiếp theo
LikeLike
Hay qá, đúng lúc e cũng đang theo js 😀
LikeLike
Sẵn nói về object anh nói thêm về clone object đi 😀
LikeLike
Clone object chỉ là copy toàn bộ object sang 1 object mới thôi mà bạn 😀
LikeLiked by 1 person
Em thấy có nhiều cách nhưng không biết cách nào có performance tốt khi clone nhiều object.
Ngoài ra khi clone có 1 thuộc tính kiểu dữ liệu object mà em muốn clone reference thì làm thế nào
LikeLike
Đôi khi làm với JavaScript, trường hợp Object thì đôi khi sẽ ức chế khi nó pass-by-reference là chủ yếu. Vd:
var a = [{x: 1}, {x: 2}];
var b = a;
b[0].x = 9
console.log(a[0].x); // 9
Nên sẽ có một vài chiêu để khi clone, chuyển sang pass-by-value. Tham khảo ví dụ dưới đây. Mình mới thử cách 1 và 2. Cách 1 thì có trường hợp nó không clone được. Cách 2 thì giải quyết được cách 1 nhưng chưa biết khi nào sẽ lỗi 😀
http://heyjavascript.com/4-creative-ways-to-clone-objects/
LikeLike
Các bác có kinh nghiệm về web cho em hỏi?
Em muốn học Jquery em có phải học JavaScrip trước không ạ?
Em cảm ơn các bác nhiều 🙂
LikeLike
Nên học để hiểu rõ nó hoạt động ra sao chứ thật ra không bắt buộc.
LikeLike
Hi bác, đọc bài viết của bác em lại liên tưởng ngay đến TypeScript. Món này có vẻ đang hot, trong TypeScript thì có class và khởi tạo object theo class, cũng như static type giống như C#, ngoài ra cũng có hỗ trợ intelliSense khi dùng IDE là VS2013 :D. Không biết bác có nghiên cứu món này chưa, hóng bài về TypeScript của bác 😀
LikeLike
Rất hay em 🙂 . Anh học thêm được nhiều cái mới 😀
LikeLike
cũng có thể sử dụng Object.create(child) để kế thừa trong JS nhé. Cụ thể mời các bác coi clip : https://www.youtube.com/watch?v=PMfcsYzj-9M
LikeLike
Bài viết hay quá. Mình cũng điên đầu về sự khác biệt giữa cái object và class. Cứ thấy thằng JS này nó không đồng nhất với các ngôn ngữ khác, nhưng chưa nói rõ ra đc 😀
LikeLike
Vì vậy nên JS nó mới sida bạn ạ 😉
LikeLike
Hóng anh một bài viết SOLID ttong javascript
LikeLike
Mấy nay đang học back end bằng node js nên xem lại bài này thấy thấm thấm hơn tý. Nhưng mà e muốn hỏi là sử dụng prototype để tạo class thì các instance mới ra đều cố định. Nếu ko làm cố định thì mình cho parameters vào. Mà cho vào rồi thì nó khác j constructor pattern đâu anh?
LikeLike
Theo như mình tìm hiểu thì trong ES6 có khái niệm class,cũng có thể tạo lớp con bằng extend mà nhỉ?
LikeLike
Nó chỉ là syntactic sugar bạn nhé. Bạn thử dùng transpiler sẽ thấy behind the scene nó vẫn sử dụng prototype nha
LikeLike