Chào mừng bạn đến với Cafedev! Trong bài viết này, chúng ta sẽ cùng khám phá cách học JavaScript từ cơ bản đến nâng cao qua việc sử dụng các `Native prototypes`. Bạn sẽ tìm hiểu về cách các prototype tích hợp sẵn như `Array.prototype` và `Object.prototype` có thể mở rộng và làm phong phú thêm các đối tượng trong JavaScript, giúp bạn viết mã hiệu quả và sáng tạo hơn. Hãy cùng Cafedev bắt đầu hành trình học tập thú vị này!
Thuộc tính "prototype"
được sử dụng rộng rãi trong lõi của JavaScript. Tất cả các hàm tạo tích hợp sẵn đều sử dụng nó.
Đầu tiên, chúng ta sẽ xem xét chi tiết, sau đó là cách sử dụng nó để thêm các khả năng mới vào các đối tượng tích hợp sẵn.
Nội dung chính
1. Object.prototype
Giả sử chúng ta xuất một đối tượng rỗng:
let obj = {};
alert( obj ); // "[object Object]" ?
Đoạn mã nào tạo ra chuỗi "[object Object]"
? Đó là phương thức toString
tích hợp sẵn, nhưng nó ở đâu? Đối tượng obj
rỗng!
…Nhưng ký hiệu ngắn gọn obj = {}
giống như obj = new Object()
, trong đó Object
là một hàm tạo đối tượng tích hợp sẵn, với prototype
của nó tham chiếu đến một đối tượng lớn với toString
và các phương thức khác.
Đây là những gì đang xảy ra:
Khi new Object()
được gọi (hoặc một đối tượng theo cú pháp {...}
được tạo), [[Prototype]]
của nó được đặt thành Object.prototype
theo quy tắc mà chúng ta đã thảo luận trong bài trước:
Vì vậy, khi obj.toString()
được gọi, phương thức này được lấy từ Object.prototype
.
Chúng ta có thể kiểm tra điều này như sau:
let obj = {};
alert(obj.__proto__ === Object.prototype); // true
alert(obj.toString === obj.__proto__.toString); //true
alert(obj.toString === Object.prototype.toString); //true
Lưu ý rằng không có [[Prototype]]
nào khác trong chuỗi phía trên Object.prototype
:
alert(Object.prototype.__proto__); // null
2. Các nguyên mẫu tích hợp khác
Các đối tượng tích hợp khác như Array
, Date
, Function
và những đối tượng khác cũng giữ các phương thức trong nguyên mẫu.
Ví dụ, khi chúng ta tạo một mảng [1, 2, 3]
, hàm tạo mặc định new Array()
được sử dụng nội bộ. Vì vậy, Array.prototype
trở thành nguyên mẫu của nó và cung cấp các phương thức. Điều này rất hiệu quả về mặt bộ nhớ.
Theo đặc tả, tất cả các nguyên mẫu tích hợp đều có Object.prototype
ở trên cùng. Đó là lý do tại sao một số người nói rằng “mọi thứ đều kế thừa từ đối tượng”.
Đây là bức tranh tổng thể (cho 3 đối tượng tích hợp để phù hợp):
Hãy kiểm tra các nguyên mẫu thủ công:
let arr = [1, 2, 3];
// it inherits from Array.prototype?
alert( arr.__proto__ === Array.prototype ); // true
// then from Object.prototype?
alert( arr.__proto__.__proto__ === Object.prototype ); // true
// and null on the top.
alert( arr.__proto__.__proto__.__proto__ ); // null
Một số phương thức trong nguyên mẫu có thể bị trùng lặp, chẳng hạn, Array.prototype
có phương thức toString
riêng liệt kê các phần tử được phân tách bằng dấu phẩy:
let arr = [1, 2, 3]
alert(arr); // 1,2,3 <-- the result of Array.prototype.toString
Như chúng ta đã thấy trước đây, Object.prototype
cũng có toString
, nhưng Array.prototype
gần hơn trong chuỗi, vì vậy biến thể của mảng được sử dụng.
Các công cụ trong trình duyệt như bảng điều khiển nhà phát triển Chrome cũng hiển thị sự kế thừa (console.dir
có thể cần được sử dụng cho các đối tượng tích hợp sẵn):
Các đối tượng tích hợp khác cũng hoạt động theo cách tương tự. Ngay cả các hàm — chúng là các đối tượng của hàm tạo tích hợp Function
, và các phương thức của chúng (call
/apply
và các phương thức khác) được lấy từ Function.prototype
. Các hàm cũng có toString
của riêng chúng.
function f() {}
alert(f.__proto__ == Function.prototype); // true
alert(f.__proto__.__proto__ == Object.prototype); // true, inherit from objects
3. Nguyên thủy
Điều phức tạp nhất xảy ra với chuỗi, số và boolean.
Như chúng ta biết, chúng không phải là các đối tượng. Nhưng nếu chúng ta cố gắng truy cập các thuộc tính của chúng, các đối tượng bọc tạm thời sẽ được tạo ra bằng cách sử dụng các constructor tích hợp String
, Number
và Boolean
. Chúng cung cấp các phương thức và biến mất.
Những đối tượng này được tạo ra một cách vô hình đối với chúng ta và hầu hết các engine tối ưu hóa chúng ra ngoài, nhưng đặc tả mô tả chính xác theo cách này. Các phương thức của những đối tượng này cũng nằm trong các prototype, có sẵn dưới dạng String.prototype
, Number.prototype
và Boolean.prototype
.
Các giá trị đặc biệt null
và undefined
đứng riêng biệt. Chúng không có các wrapper đối tượng, vì vậy các phương thức và thuộc tính không có sẵn cho chúng. Và cũng không có các prototype tương ứng.
4. Thay đổi các native prototypes
Các prototype gốc có thể được chỉnh sửa. Ví dụ, nếu chúng ta thêm một phương thức vào String.prototype
, phương thức đó sẽ có sẵn cho tất cả các chuỗi.
String.prototype.show = function() {
alert(this);
};
"BOOM!".show(); // BOOM!
Trong quá trình phát triển, chúng ta có thể có ý tưởng về các phương thức tích hợp mới mà chúng ta muốn có, và chúng ta có thể bị cám dỗ để thêm chúng vào các prototype tích hợp. Nhưng đó thường là một ý tưởng không tốt.
Các prototype là toàn cục, vì vậy rất dễ xảy ra xung đột. Nếu hai thư viện thêm một phương thức
String.prototype.show`, thì một trong số chúng sẽ ghi đè phương thức của thư viện còn lại.
Vì vậy, nói chung, việc thay đổi một prototype tích hợp được coi là một ý tưởng không tốt.
Trong lập trình hiện đại, chỉ có một trường hợp duy nhất mà việc thay đổi các prototype tích hợp được chấp nhận. Đó là polyfilling.
Polyfilling là thuật ngữ chỉ việc tạo một sự thay thế cho một phương thức tồn tại trong đặc tả JavaScript, nhưng chưa được hỗ trợ bởi một engine JavaScript cụ thể.
Chúng ta có thể thực hiện nó một cách thủ công và bổ sung vào prototype tích hợp.
Ví dụ:
if (!String.prototype.repeat) { // if there's no such method
// add it to the prototype
String.prototype.repeat = function(n) {
// repeat the string n times
// actually, the code should be a little bit more complex than that
// (the full algorithm is in the specification)
// but even an imperfect polyfill is often considered good enough
return new Array(n + 1).join(this);
};
}
alert( "La".repeat(3) ); // LaLaLa
5. Mượn prototypes
Trong bài Decorators và forwarding, call/apply, chúng ta đã nói về việc mượn phương thức
Đó là khi chúng ta lấy một phương thức từ một đối tượng và sao chép nó vào một đối tượng khác. Được gọi là mượn phương thức
Một số phương thức của các prototype tích hợp sẳn thường được mượn.
Ví dụ, nếu chúng ta đang tạo một đối tượng giống như mảng, chúng ta có thể muốn sao chép một số phương thức của Array
vào đó.
Ví dụ:
let obj = {
0: "Hello",
1: "world!",
length: 2,
};
*!*
obj.join = Array.prototype.join;
*/!*
alert( obj.join(',') ); // Hello,world!
Nó hoạt động vì thuật toán nội bộ của phương thức tích hợp join
chỉ quan tâm đến các chỉ mục chính xác và thuộc tính length
. Nó không kiểm tra xem đối tượng có thực sự là một mảng hay không. Nhiều phương thức tích hợp hoạt động như vậy.
Một khả năng khác là kế thừa bằng cách đặt obj.__proto__
thành Array.prototype
, để tất cả các phương thức Array
đều có sẵn trong obj
.
Nhưng điều đó không khả thi nếu obj
đã kế thừa từ một đối tượng khác. Hãy nhớ rằng, chúng ta chỉ có thể kế thừa từ một đối tượng tại một thời điểm.
Việc mượn các phương thức rất linh hoạt, cho phép kết hợp các chức năng từ các đối tượng khác nhau nếu cần.
6. Tóm tắt
- Tất cả các đối tượng tích hợp sẵn đều đi kèm theo cùng một mẫu:
- Các phương thức được lưu trữ trong prototype (
Array.prototype
,Object.prototype
,Date.prototype
, v.v.) - Đối tượng tự lưu trữ dữ liệu (các phần tử mảng, thuộc tính đối tượng, ngày tháng)
- Các phương thức được lưu trữ trong prototype (
- Các kiểu dữ liệu nguyên thủy cũng lưu trữ các phương thức trong prototypes của các đối tượng bọc:
Number.prototype
,String.prototype
vàBoolean.prototype
. Chỉ cóundefined
vànull
là không có đối tượng bọc - Các prototypes tích hợp sẵn có thể được sửa đổi hoặc bổ sung thêm các phương thức mới. Nhưng không khuyến khích thay đổi chúng. Trường hợp duy nhất có thể chấp nhận là khi chúng ta thêm một tiêu chuẩn mới, nhưng chưa được hỗ trợ bởi engine JavaScript
Chúng ta đã cùng nhau khám phá các `Native prototypes` trong JavaScript từ những điều cơ bản đến nâng cao. Hy vọng rằng các kiến thức này sẽ giúp bạn nắm vững cách sử dụng và mở rộng các đối tượng trong JavaScript. Cảm ơn bạn đã đồng hành cùng Cafedev trong hành trình học tập này. Hãy tiếp tục theo dõi Cafedev để cập nhật thêm nhiều kiến thức hữu ích và các bài viết thú vị khác!
Tham khảo thêm: MIỄN PHÍ 100% | Series tự học Javascrypt chi tiết, dễ hiểu từ cơ bản tới nâng cao + Full Bài Tập thực hành nâng cao trình dev
Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây
Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:
Chào thân ái và quyết thắng!