Ứng dụng Prototype trong JavaScript viết Slideshow

964
03-03-2018
Ứng dụng Prototype trong JavaScript viết Slideshow

Giới thiệu Prototype

Chắc mọi người ai cũng đã từng nghe qua về khái niệm Prototype trong JavaScript rồi phải không?

Nếu chưa biết về khái niệm này thì các bạn đọc Bizfly Cloud có thể tham khảo tài liệu  dưới đây:

• https://toidicodedao.com/2016/02/02/series-javascript-sida-po-ro-to-tai-prototype-la-cai-gi/

• https://kipalog.com/posts/prototype-la-khi-gi/

Tóm lại, Prototype là cách để thực hiện việc kế thừa 1 Object trong JavaScript. Chúng ta ai cũng hiểu về OOP nhưng để áp dụng OOP vào JS là 1 chuyện khá lấn cấn, chưa quen, do đặc thù ngôn ngữ JS bị "si-đa" (theo bác Phạm Huy Hoàng - toidicodedao.com), đúng thật là người ta viết JS theo hướng FP (Function Programming) như 1 thói quen từ lâu khó bỏ.
Do vậy, bài viết này giúp các bạn áp dụng kiến thức Prototype để tự viết 1 Slideshow hình ảnh đơn giản nhằm có cái nhìn thực tiễn hơn về OOP trong JS. 
bài viết gồm 2 phần:
Phần 1 - viết slider theo hướng function - cách tiếp cận phổ biến của mọi người
Phần 2 - tối ưu hóa slider theo hướng OOP - sử dụng prototype.
Ứng dụng prototype trong JavaScript viết Slideshow

Ảnh 1.

Viết Slider dạng function

Slider của chúng ta là 1 trình chiếu hình ảnh, rất quen thuộc trong các website. Có 2 cái nút để chuyển hình.
Đầu tiên là phần HTML:

<div class="slider"> <ul> <li><img src="https://via.placeholder.com/400x200" alt="image"></li> <li><img src="https://via.placeholder.com/400x200" alt="image"></li> <li><img src="https://via.placeholder.com/400x200" alt="image"></li> <li><img src="https://via.placeholder.com/400x200" alt="image"></li> </ul> </div> <div id="slider-nav"> <button data-dir="prev">Previous</button> <button data-dir="next">Next</button> </div>

Nhúng thư viện jQuery giúp việc code dễ dàng hơn Tách code slider của chúng ta ra file riêng.

- Phần JavaScript/jQuery: http://jsfiddle.net/a0Luphbk/6/

Phân tích đoạn code JS trên: Đầu tiên là bao ngoài phần code của chúng ta là 1 anonymous function có tác dụng tránh đụng độ với các thư viện sử dụng dấu $ khác với jQuery như Mootools hay PrototypeJS
Sau đó là khai báo các biến cần thiết cho slider.
Khi nhấn vào nút Next, ta nhận biết được nhờ vào data-dir="next", ta sẽ tăng biến vị trí hiện tại lên 1.
Hàm Transition có tác dụng hiệu ứng, dịch chuyển bức hình số 1 sang trái 200px (imgWidth) - sẽ hiển thị tấm hình số 2.

Ảnh 3.

Tối ưu hóa Prototype

Bây giờ ta sẽ viết Function trên thành Class Slider thuần OOP
Constructor. Thay vì Select Element trong hàm như phần 1, tôi sẽ truyền vào Constructor của Class Slider:

function Slider(container, nav) { this.container = container; this.imgs = this.container.find('img'); this.imgWidth = this.imgs[0].width; this.imgsLen = this.imgs.length; this.current = 0; this.events.click.call(this); }

Với Container là $('.slider').children('ul')
Và nav là $('#slider-nav').find('button')
Vẫn là biến imgs và kích thước của nó.
Current lúc này ta chọn gốc = 0
this.events.click.call(this) - ta dùng call tác dụng đảm bảo luôn truyền vào class Slider là this trong method events.click
Xong phần Constructor, ta sẽ viết tiếp phần event.click

Slider.prototype.events = { click: function() { var self = this; self.nav.find('button').on('click', function(){ self.setCurrent( $(this).data('dir') ); self.transition(); }); } };

Ở đây ta đã áp dụng kiến thức Prototype được học, định nghĩa thêm 1 Method vào Class có sẵn Slider.
Events ở đây thực chất là 1 Object với Method là click (nếu khi gọi hàm click trong constructor, ta không dùng call, thì this sẽ hiểu là function click).
Event click này làm 2 việc là setCurrent và hiệu ứng Transition. Tính ra thì công việc cũng y hệt với phần click khi viết bằng Function, tuy nhiên nhìn sáng sủa, rõ ràng hơn rất nhiều.

Tiếp theo ta định nghĩa Method setCurrent

Slider.prototype.setCurrent = function( dir ) { var pos = this.current; // if direction == next then pos else pos-- pos = (dir === 'next' ? 1 : -1); // reset current if pos < 0, 0 is start position this.current = (pos < 0) ? this.imgsLen - 1 : pos % this.imgsLen; return pos; }

Dòng code reset lại this.current nếu nó < 0 ngược lại thì current sẽ là modulo cho 4.

Tiếp theo ta định nghĩa Method Transition:

Slider.prototype.transition = function() { this.container.animate({ 'margin-left': -(this.current * this.imgWidth) }); }

Method này cũng ngắn hơn phiên bản đầu tiên. 
Ứng với bức hình thứ 3, current sẽ bằng 2 (bắt đầu từ 0), 2 * 400 = 800 => margin-left: 

-800px |400|400|400|400| here

Khác với phương pháp Function, phương pháp OOP chúng ta phải khởi tạo Instance từ Object Slider và truyền vào 2 tham số:

var slider = new Slider( $('.slider').find('ul'), $('#slider-nav') );

Các bạn có thể tham khảo bộ code tối ưu hóa Slider bằng Prototype này tại đây: https://jsfiddle.net/r6x3sLb1/2/

Vậy là ta đã viết được 1 Slideshow trình chiếu hình ảnh đơn giản, áp dụng kiến thức prototype được học.
Phải công nhận rằng, Code js viết theo OOP nhìn sáng sủa và dễ hiểu hơn Function rất nhiều, nhất là các chức năng phức tạp.
Học đi đôi với hành, hy vọng bạn sẽ cải tiến Object Slider này để nó càng linh hoạt hơn, càng nhiều Option hơn nữa, cũng như tự viết được nhiều Object js theo hướng OOP.

Qua bài Prototype này có 1 câu hỏi giành cho các bạn: tại sao Slider lại phải viết Method transition, setCurrent, events... bên ngoài thông qua Prototype? Sao không viết ngay bên trong Object như:

function Slider(container, nav) { // constructor this.transition = function() {} this.setCurrent = function() {} this.events = function() {} } Why?
Ảnh 11.

Còn khi ta dùng Prototype, thì những Method này không tạo ra khi khởi tạo Instance, nó tồn tại sẵn trong Prototype, tức là Slider.prototype. Và chỉ khi Instance gọi tới this.events.click.call(this) thì Method này mới được sử dụng, this tương ứng với var slider.
Thứ 2, các Method này chia sẻ chung cho toàn bộ các Instance khởi tạo từ Object Slider
Ở đây đang nói tới điểm lợi dùng Prototype chính là bộ nhớ sử dụng - Performance. Chúng ta chỉ viết Method bên trong Function Constructor khi và chỉ khi Method đó truy xuất tới các biến cục bộ (Private) và dành riêng cho mỗi Instance.

Nguồn: kipalog.com

SHARE