Toán tử instanceof
cho phép kiểm tra một đối tượng có thuộc lớp nào đó hay không. Và toán tử này cũng tính đến kế thừa.
Việc kiểm tra kiểu của class là cần thiết trong nhiều trường hợp. Ví dụ, toán tử instanceof
có thể được sử dụng để xây dựng một hàm đa hình – một hàm xử lý các đối số khác nhau tùy thuộc vào kiểu của chúng.
Tóm Tắt
Toán tử instanceof
Cú pháp sử dụng toán tử instanceof
là:
obj instanceof
Class
;
Câu lệnh trên trả về true
nếu obj
thuộc về Class
hoặc một lớp kế thừa từ Class
đó, ví dụ:
class
Rabbit
{
}
let
rabbit =
new
Rabbit
(
)
;
console.
log
(
rabbit instanceof
Rabbit
)
;
Toán tử instanceof
cũng hoạt động với các hàm khởi tạo như sau:
function
Rabbit
(
)
{
}
console.
log
(
new
Rabbit
(
)
instanceof
Rabbit
)
;
Và kể cả các built-in class như Array
:
let
arr =
[
1
,
2
,
3
]
;
console.
log
(
arr instanceof
Array
)
;
console.
log
(
arr instanceof
Object
)
;
Chú ý: arr
cũng thuộc về lớp Object
. Đó là bởi vì Array
kế thừa nguyên mẫu – kế thừa prototype từ Object
.
Thông thường, toán tử instanceof
dựa trên chuỗi prototype để kiểm tra class. Ngoài ra, bạn cũng có thể tùy chỉnh phương thức static Symbol.hasInstance
trong class.
Bởi vì, thuật toán của obj instanceof Class
hoạt động theo cách sau:
- Nếu class có một phương thức tĩnh là
Symbol.hasInstance
, thì toán tửinstanceof
chỉ cần gọi phương thức đó dạngClass[Symbol.hasInstance](obj)
. Và kết quả trả về sẽ làtrue
hoặcfalse
. Đó chính là cách để tùy chỉnh hoạt động của toán tửinstanceof
, ví dụ:
class
Animal
{
static
[
Symbol.
hasInstance]
(
obj
)
{
if
(
obj.
canEat)
{
return
true
;
}
return
false
;
}
}
let
obj =
{
canEat
:
true
}
;
console.
log
(
obj instanceof
Animal
)
;
- Hầu hết các class không có phương thức
Symbol.hasInstance
. Trong trường hợp đó, logic cơ bản được sử dụng choobj instanceOf Class
là kiểm tra xemClass.prototype
có bằng một trong các prototype trong chuỗi prototype củaobj
hay không. Nói cách khác, thuật toán để kiểm tra như sau:
obj.
__proto__ ===
Class
.
prototype;
obj.
__proto__.
__proto__ ===
Class
.
prototype;
obj.
__proto__.
__proto__.
__proto__ ===
Class
.
prototype;
Trong ví dụ trên rabbit.__proto__ === Rabbit.prototype
, do đó kết quả là true
ngay lập tức.
Với trường hợp kế thừa, quá trình so sánh sẽ dừng lại ở bước thứ hai:
class
Animal
{
}
class
Rabbit
extends
Animal
{
}
let
rabbit =
new
Rabbit
(
)
;
console.
log
(
rabbit instanceof
Animal
)
;
Ngoài toán tử instanceof
, cũng có một phương thức objA.isPrototypeOf(objB), trả về true
nếu objA
ở đâu đó trong chuỗi prototype của objB
.
Vì vậy, obj instanceof Class
có thể được thay thế bằng câu lệnh Class.prototype.isPrototypeOf(obj)
.
Chú ý: Bản thân hàm khởi tạo
Class
không tham gia vào việc kiểm tra.Chỉ có chuỗi prototype và
Class.prototype
được sử dụng với toán tửinstanceof
.
Điều đó có thể dẫn đến những kết quả không mong muốn khi thuộc tính prototype
bị thay đổi sau khi đối tượng được tạo ra, như sau:
function
Rabbit
(
)
{
}
let
rabbit =
new
Rabbit
(
)
;
Rabbit
.
prototype =
{
}
;
console.
log
(
rabbit instanceof
Rabbit
)
;
Đôi điều về Object.prototype.toString
Bạn biết rằng các object thuần (plain object) được chuyển đổi thành string dưới dạng [object Object]
, ví dụ:
let
obj =
{
}
;
alert
(
obj)
;
console.
log
(
obj.
toString
(
)
)
;
Đó chính là cách JavaScript triển khai phương thức toString
.
Tuy nhiên, có một tính năng ẩn làm cho toString
thực sự linh hoạt hơn thế. Bạn có thể sử dụng toString
như một cách mở rộng của toán tử typeof
để thay thế cho instanceof
.
Theo đặc tả của toString
, phương thức này có thể được lấy ra từ object và thực thi trong ngữ cảnh (context) của bất kỳ giá trị (kiểu dữ liệu) nào khác. Và kết quả thu được sẽ phụ thuộc vào giá trị đó.
- Đối với một số, kết quả là
[object Number]
. - Đối với boolean, kết quả là
[object Boolean]
- Đối với
null
là:[object Null]
. - Đối với
undefined
là:[object Undefined]
. - Đối với mảng là
[object Array]
. - …vv (có thể tùy chỉnh).
Bạn có thể xem ví dụ sau để thấy rõ:
let
objectToString =
Object
.
prototype.
toString;
let
arr =
[
]
;
console.
log
(
objectToString
.
call
(
arr)
)
;
Cụ thể, mình đã sử dụng hàm call
như được mô tả từ bài function binding trong JavaScript. Ở đây, thuật toán toString
kiểm tra this
và trả về kết quả tương ứng.
Ngoài ra còn các ví dụ khác:
let
s =
Object
.
prototype.
toString;
console.
log
(
s
.
call
(
123
)
)
;
console.
log
(
s
.
call
(
null
)
)
;
console.
log
(
s
.
call
(
console.
log)
)
;
Hoạt động của phương thức toString
trong object có thể được tùy chỉnh bằng cách sử dụng một symbol đặc biệt là Symbol.toStringTag
.
Ví dụ:
let
user =
{
[
Symbol.
toStringTag]
:
"User"
,
}
;
console.
log
(
{
}
.
toString
.
call
(
user)
)
;
Hầu hết các đối tượng của môi trường cụ thể (trình duyệt, Node.js,…) đều có một thuộc tính như vậy. Và dưới đây là ví dụ về một số đối tượng trên trình duyệt:
console.
log
(
window[
Symbol.
toStringTag]
)
;
console.
log
(
XMLHttpRequest
.
prototype[
Symbol.
toStringTag]
)
;
console.
log
(
{
}
.
toString
.
call
(
window)
)
;
console.
log
(
{
}
.
toString
.
call
(
new
XMLHttpRequest
(
)
)
)
;
Như bạn thấy, kết quả chính xác là Symbol.toStringTag
(nếu tồn tại) và có dạng [object...]
.
Tóm lại, bạn có thể sử dụng {}.toString.call
thay vì instanceof
cho các đối tượng có sẵn khi muốn lấy kiểu dưới dạng string thay vì chỉ để kiểm tra.
Tổng kết
Dưới đây là tóm tắt các phương pháp kiểm tra kiểu (class):
Áp dụng cho
Kiểu trả về
typeof
Kiểu nguyên thủy
string
{}.toString
Kiểu nguyên thủy, built-in class, đối tượng có Symbol.toStringTag
string
instanceof
Các đối tượng
boolean
Như bạn có thể thấy, {}.toString
về cơ bản là nâng cao hơn so với typeof
.
Và toán tử instanceof
thực sự hiệu quả khi bạn đang làm việc với hệ thống các class có phân cấp và muốn kiểm tra class có tính đến tính kế thừa.
Tham khảo: Class checking: “instanceof”