Mặc dù trong công việc gần đây không thường xuyên sử dụng zepto, nhưng được biết rằng nguồn mã của zepto khá đơn giản và có nhiều tài liệu tham khảo trên mạng, nên tôi đã chọn zepto để bắt đầu. Hy vọng đây sẽ là nền tảng để đọc hiểu nguồn mã của các framework khác trong tương lai.
Phiên bản mã nguồn
Bài viết này phân tích phiên bản zepto 1.2.0
Trước khi tìm hiểu zepto, cần có kiến thức về chuỗi prototype và closure trong JavaScript. Tôi khuyên nên đọc bài viết của tác giả Vương Phủ Bằng: Hiểu sâu về Prototype và Closure trong JavaScript, bài viết này rất chi tiết và dễ tiếp thu.
Cấu trúc mã nguồn
Cấu trúc tổng thể
var Zepto = (function () {
...
})()
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)
Nếu bạn thu gọn mã nguồn zepto trong trình soạn thảo, cấu trúc sẽ giống như trên.
Trọng tâm của zepto là một closure, được thực thi ngay sau khi tải xong. Sau đó, nó được gán cho biến toàn cục zepto, và nếu $ chưa được định nghĩa, thì $ cũng sẽ được gán giá trị của zepto.
Cấu trúc cốt lõi
Trong phần này, chúng ta sẽ không tập trung vào việc triển khai cụ thể của zepto, mà chỉ xem xét cấu trúc cốt lõi. Vì vậy, tôi sẽ tạm thời loại bỏ logic trong zepto để có được cấu trúc cốt lõi như sau:
var zepto = {}, $
function Z(doms) {
var len = doms.length
for (var i = 0; i < len; i++) {
this[i] = doms[i]
}
this.length = doms.length
}
zepto.Z = function(doms) {
return new Z(doms)
}
zepto.init = function(doms) {
var elements = ['element1','element2','element3']
return zepto.Z(elements)
}
$ = function() {
return zepto.init()
}
$.fn = {
constructor: zepto.Z,
process: function() {
return this
}
}
zepto.Z.prototype = Z.prototype = $.fn
return $
Trong mã nguồn, có thể thấy rằng $ thực chất là một hàm, đồng thời trên $ cũng có nhiều thuộc tính và phương thức được đính kèm (đây được thể hiện ở $.fn, các phương thức khác sẽ được đề cập trong các bài viết sau).
Khi sử dụng zepto, chúng ta dùng $ để lấy dom, và trên các đối tượng dom này đều có các phương thức thao tác được định nghĩa bởi zepto.
Từ đoạn mã trên, có thể thấy $ thực chất gọi phương thức zepto.init(), trong phương thức init sẽ lấy tập hợp các phần tử dom, sau đó chuyển tập hợp này cho phương thức zepto.Z() xử lý, còn phương thức zepto.Z trả về một thực thể của hàm Z.
Hàm Z sẽ mở rộng doms thành các thuộc tính của thực thể, với key là chỉ số tương ứng của element, và thiết lập thuộc tính length của thực thể.
zepto.Z.prototype = Z.prototype = $.fn
Đọc đến đây, bạn có thể hơi thắc mắc, $ cuối cùng trả về là một thực thể của hàm Z, nhưng hàm Z rõ ràng không có các phương thức thao tác với dom đâu, các phương thức này đều được định nghĩa trên $.fn, tại sao $ có thể gọi các phương thức này?
Thực chất, chìa khóa nằm ở dòng code Z.prototype = $.fn, dòng code này gán prototype của Z trỏ đến $.fn, như vậy, thực thể của Z đã kế thừa các phương thức của $.fn.
Nếu chúng ta xem tiếp mã nguồn, sẽ thấy có một phương thức như sau:
zepto.isZ = function(object) {
return object instanceof zepto.Z
}
Phương thức này dùng để kiểm tra một đối tượng có phải là đối tượng zepto không, việc này được thực hiện bằng cách kiểm tra đối tượng đó có phải là thực thể của zepto.Z hay không. Vì vậy, cần gán prototype của zepto.Z và Z trỏ đến cùng một đối tượng. Phương thức isZ sẽ được sử dụng trong init, sau này sẽ được giới thiệu.
Tham khảo
- Phân tích mã nguồn zepto - Cấu trúc code
- Tư tưởng đối tượng zepto và phân tích mã nguồn
- Thiết kế zepto và phân tích mã nguồn
- Vấn đề về
zepto.Z.prototype = $.fntrong mã nguồn zepto