Boxing
Trong nhiều ngôn ngữ, primitive thường không có field hay method. Để xử lý primitive như object, cần chuyển đổi primitive sang object. Việc chuyển đổi từ primitive sang object được gọi là boxing.
ts// Kiểu primitiveconststr = "abc";// Đưa vào wrapper objectconststrObject = newString (str );// Xử lý như objectstrObject .length ; // Tham chiếu fieldstrObject .toUpperCase (); // Gọi method
ts// Kiểu primitiveconststr = "abc";// Đưa vào wrapper objectconststrObject = newString (str );// Xử lý như objectstrObject .length ; // Tham chiếu fieldstrObject .toUpperCase (); // Gọi method
Ví dụ trên minh họa khái niệm boxing trong JavaScript. Trong code thực tế, không cần phải bỏ primitive type vào wrapper object như String. Vì JavaScript có cơ chế auto-boxing.
Auto-boxing
Trong JavaScript, ngay cả giá trị primitive type cũng có thể truy cập field và gọi method.
tsconststr = "abc";// Xử lý như objectstr .length ; // Tham chiếu fieldstr .toUpperCase (); // Gọi method
tsconststr = "abc";// Xử lý như objectstr .length ; // Tham chiếu fieldstr .toUpperCase (); // Gọi method
Giá trị primitive type không phải object, nên việc có thể thao tác như vậy là điều lạ. Có vẻ cần boxing. Tuy nhiên điều này có thể thực hiện được là vì JavaScript nội bộ chuyển đổi giá trị primitive type sang object. Việc chuyển đổi ngầm định này gọi là auto-boxing.
Wrapper object
Object được chuyển đổi đến trong auto-boxing của JavaScript gọi là wrapper object. Tương ứng giữa primitive type và wrapper object như bảng sau.
| Primitive type | Wrapper object |
|---|---|
boolean | Boolean |
number | Number |
string | String |
symbol | Symbol |
bigint | BigInt |
Primitive type undefined và null không có wrapper object. Do đó việc truy cập method hay field luôn phát sinh lỗi.
tsThe value 'null' cannot be used here.18050The value 'null' cannot be used here.null. toString ();The value 'undefined' cannot be used here.18050The value 'undefined' cannot be used here.. undefined toString ();
tsThe value 'null' cannot be used here.18050The value 'null' cannot be used here.null. toString ();The value 'undefined' cannot be used here.18050The value 'undefined' cannot be used here.. undefined toString ();
Cách đọc MDN
Document thường được dùng khi học JavaScript là MDN Web Docs. Khi hiểu về auto-boxing và wrapper object, document MDN sẽ dễ hiểu hơn.
Ví dụ, giải thích về method toString của số được viết trong MDN tại trang có tiêu đề 「Number.prototype.toString()」. Nếu nghĩ toString là thứ có trong primitive type number, có thể có thắc mắc như "Number.prototype là gì", "Đang tìm hiểu number type mà tại sao lại viết trong trang Number object".
Khi biết về auto-boxing và wrapper object, thắc mắc này được giải quyết. number không có method cũng không có field. Có vẻ như có method là do auto-boxing chuyển number sang Number object. Vì vậy việc giải thích toString viết trong trang Number object là hợp lý. Ngoài ra cũng hiểu được ý nghĩa Number.prototype biểu thị là "có trong instance của Number object".
Wrapper object type và type của TypeScript
Trong TypeScript, type của wrapper object cũng được định nghĩa. Có thể viết type annotation bằng wrapper object type như sau. Cũng có thể gán giá trị primitive type vào biến wrapper object type.
tsconstbool :Boolean = false;constnum :Number = 0;conststr :String = "";constsym :Symbol =Symbol ();constbig :BigInt = 10n;
tsconstbool :Boolean = false;constnum :Number = 0;conststr :String = "";constsym :Symbol =Symbol ();constbig :BigInt = 10n;
Tuy nhiên wrapper object type không thể gán vào primitive type.
tsconstn1 :Number = 0;constType 'Number' is not assignable to type 'number'. 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible.2322Type 'Number' is not assignable to type 'number'. 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible.: number = n2 n1 ;
tsconstn1 :Number = 0;constType 'Number' is not assignable to type 'number'. 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible.2322Type 'Number' is not assignable to type 'number'. 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible.: number = n2 n1 ;
Wrapper object type không dùng được operator.
tsconstnum :Number = 1;The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.2362The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.* 2; num
tsconstnum :Number = 1;The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.2362The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.* 2; num
Wrapper object type có thể gán các giá trị khác ngoài primitive type, miễn là object đáp ứng interface đó.
tsconstboolLike = {valueOf (): boolean {return true;},};constbool :Boolean =boolLike ;
tsconstboolLike = {valueOf (): boolean {return true;},};constbool :Boolean =boolLike ;
Không có lợi ích gì khi dùng wrapper object type cho type annotation thay vì primitive type. Hãy dùng primitive type cho type annotation.
ts// ❌ Saiconstnum1 :Number = 0;// ✅ Đúngconstnum2 : number = 0;
ts// ❌ Saiconstnum1 :Number = 0;// ✅ Đúngconstnum2 : number = 0;
Chia sẻ kiến thức
・Boxing là việc chuyển đổi primitive sang object
・Primitive trong JavaScript có thể xử lý như object nhờ auto-boxing
・Trong TypeScript nên type annotation bằng primitive type (ví dụ: string) hơn là wrapper object (ví dụ: String)
Từ 『Survival TypeScript』
Thông tin liên quan
📄️ Kiểu primitive
Data type trong JavaScript được phân loại thành primitive type và object.