Các toán từinstanceof
cho phép để kiểm tra xem một đối tượng thuộc về một lớp nhất định nào vậy. Nó cũng có tính kế thừa.
Kiểm tra như vậy có thể cần thiết trong nhiều trường hợp. Ở đây chúng ta sẽ sử dụng nó để 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.
Nội dung chính
1. Toán tử instanceof
Cú pháp là:
obj instanceof Class
Nó trả về true
nếu obj
thuộc về Class
hoặc một lớp kế thừa từ nó.
Ví dụ:
class Rabbit {}
let rabbit = new Rabbit();
// is it an object of Rabbit class?
alert( rabbit instanceof Rabbit ); // true
Nó cũng hoạt động với các hàm xây dựng:
// instead of class
function Rabbit() {}
alert( new Rabbit() instanceof Rabbit ); // true
… và với các lớp dựng sẵn như Array
:
let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
Xin lưu ý rằng arr
cũng thuộc về lớpObject
. Đó là bởi vì nguyên mẫu Array
được thừa hưởng từ Object
.
Thông thường, instanceof
kiểm tra một chuỗi nguyên mẫu. Chúng ta cũng có thể thiết lập một logic tùy chỉnh trong phương thức tĩnh Symbol.hasInstance
.
Thuật toán của các công trình obj instanceof Class
đại khái như sau:
- Nếu có một phương thức tĩnh
Symbol.hasInstance
, thì chỉ cần gọi nó :Class[Symbol.hasInstance](obj)
. Nó sẽ trả lạitrue
hoặcfalse
, và chúng ta đã hoàn thành. Đó là cách chúng ta có thể tùy chỉnh hành vi củainstanceof
. Ví dụ:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
// setup instanceOf check that assumes that
// anything with canEat property is an animal
class Animal {
static [Symbol.hasInstance](obj) {
if (obj.canEat) return true;
}
}
let obj = { canEat: true };
alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) is called
Hầu hết các lớp không có Symbol.hasInstance
. Trong trường hợp đó, logic chuẩn được sử dụng: obj instanceOf Class
để kiểm tra xem Class.prototype
có bằng một trong các nguyên mẫu trong chuỗi nguyên mẫu củaobj
hay không.
Nói cách khác, so sánh cái này với cái khác:
obj.__proto__ === Class.prototype?
obj.__proto__.__proto__ === Class.prototype?
obj.__proto__.__proto__.__proto__ === Class.prototype?
...
// if any answer is true, return true
// otherwise, if we reached the end of the chain, return false
Trong ví dụ trên rabbit.__proto__ === Rabbit.prototype
, do đó đưa ra câu trả lời ngay lập tức.
Trong trường hợp thừa kế, sẽ ở bước thứ hai:
class Animal {}
class Rabbit extends Animal {}
let rabbit = new Rabbit();
alert(rabbit instanceof Animal); // true
// rabbit.__proto__ === Rabbit.prototype
// rabbit.__proto__.__proto__ === Animal.prototype (match!)
Dưới đây là minh họa về những gì rabbit instanceof Animal
so sánh với Animal.prototype
:
Nhân tiện, cũng có một phương thức objA.isPrototypeOf(objB), trả về true
nếu objA
có ở đâu đó trong chuỗi các nguyên mẫu của objB
. Vì vậy, bài kiểm tra obj instanceof Class
có thể được như Class.prototype.isPrototypeOf(obj)
.
Thật buồn cười, nhưng bản thân constructor của Class
không tham gia kiểm tra! Chỉ có chuỗi các nguyên mẫu và Class.prototype
.
Điều đó có thể dẫn đến hậu quả thú vị khi một thuộc tính prototype
được thay đổi sau khi đối tượng được tạo.
Giống như ở đây:
function Rabbit() {}
let rabbit = new Rabbit();
// changed the prototype
Rabbit.prototype = {};
// ...not a rabbit any more!
alert( rabbit instanceof Rabbit ); // false
2. Phần thêm: Object.prototype.toString cho các kiểu
Chúng ta đã biết rằng các đối tượng đơn giản được chuyển đổi thành chuỗi dưới dạng [object Object]
:
let obj = {};
alert(obj); // [object Object]
alert(obj.toString()); // the same
Đó là việc họ thực hiện toString
. Nhưng có một tính năng ẩn toString
thực sự mạnh hơn thế nhiều. Chúng ta có thể sử dụng nó như một phần mở rộng typeof
và thay thế cho instanceof
.
Nghe khá lạ? Thật. Hãy làm sáng tỏ.
Theo đặc tả, tích hợp toString
có thể được trích xuất từ đối tượng và được thực thi trong ngữ cảnh của bất kỳ giá trị nào khác. Và kết quả của nó phụ thuộc vào giá trị đó.
- Đối với một số, nó sẽ là
[object Number]
- Đối với một boolean, nó sẽ là
[object Boolean]
- Dành cho
null
:[object Null]
- Dành cho
undefined
:[object Undefined]
- Đối với mảng:
[object Array]
- Vv vv (tùy biến).
Hãy chứng minh:
// copy toString method into a variable for convenience
let objectToString = Object.prototype.toString;
// what type is this?
let arr = [];
alert( objectToString.call(arr) ); // [object Array]
Ở đây chúng ta đã sử dụng cuộc gọi như được mô tả để thực hiện hàm objectToString
trong ngữ cảnh this=arr
.
Trong nội bộ, toString
thuật toán kiểm tra this
và trả về kết quả tương ứng. Ví dụ khác:
let s = Object.prototype.toString;
alert( s.call(123) ); // [object Number]
alert( s.call(null) ); // [object Null]
alert( s.call(alert) ); // [object Function]
2.1. Symbol.toStringTag
Hành vi của Object toString
có thể được tùy chỉnh bằng cách sử dụng một thuộc tính đối tượng đặc biệt Symbol.toStringTag
.
Ví dụ:
let user = {
[Symbol.toStringTag]: "User"
};
alert( {}.toString.call(user) ); // [object User]
Đối với hầu hết các đối tượng môi trường cụ thể, có một thuộc tính như vậy. Dưới đây là một số ví dụ cụ thể về trình duyệt:
// toStringTag for the environment-specific object and class:
alert( window[Symbol.toStringTag]); // window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
Như bạn có thể thấy, kết quả là chính xác Symbol.toStringTag
(nếu tồn tại), được gói vào [object ...]
.
Cuối cùng, chúng ta có loại kiểu chuỗi, không chỉ hoạt động cho các kiểu dữ liệu nguyên thủy, mà còn cho các đối tượng tích hợp và thậm chí có thể được tùy chỉnh.
Chúng ta có thể sử dụng {}.toString.call
thay vì instanceof
cho các đối tượng tích hợp khi chúng ta muốn lấy kiểu dưới dạng chuỗi thay vì chỉ để kiểm tra.
3. Tóm lược
Hãy tóm tắt các phương thức kiểm tra kiểu mà chúng ta biết:
làm việc cho | trả lại | |
---|---|---|
typeof | Kiểu nguyên thủy | chuỗi |
{}.toString | Kiểu nguyên thủy, đối tượng tích hợp, đối tượng với Symbol.toStringTag | chuỗi |
instanceof | các đối tượng | đúng sai |
Như chúng ta có thể thấy, {}.toString
về mặt kỹ thuật là một nâng cao hơn nữa typeof
.
Và toán tử instanceof
thực sự tỏa sáng khi chúng ta đang làm việc với một hệ thống phân cấp lớp và muốn kiểm tra lớp có tính đến sự kế thừa.
Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.
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!