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.

1. Toán tử instanceof

Cú pháp là:

obj instanceof Class

Nó trả về truenếu objthuộc về Classhoặ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 arrcũ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, instanceofkiể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:

  1. 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ại truehoặc false, và chúng ta đã hoàn thành. Đó là cách chúng ta có thể tùy chỉnh hành vi của instanceof. 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.prototypecó 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 Animalso sánh với Animal.prototype:

Nhân tiện, cũng có một phương thức objA.isPrototypeOf(objB), trả về truenếu objAcó ở đâu đó trong chuỗi các nguyên mẫu của objB. Vì vậy, bài kiểm tra obj instanceof Classcó 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 toStringthự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 typeofvà thay thế cho instanceof.

Nghe khá lạ? Thật. Hãy làm sáng tỏ.

Theo đặc tả, tích hợp toStringcó 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 objectToStringtrong ngữ cảnh this=arr.

Trong nội bộ, toStringthuật toán kiểm tra thisvà 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 toStringcó 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.callthay vì instanceofcho 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 chotrả lại
typeofKiểu nguyên thủychuỗi
{}.toStringKiểu nguyên thủy, đối tượng tích hợp, đối tượng với Symbol.toStringTagchuỗi
instanceofcác đối tượngđúng sai

Như chúng ta có thể thấy, {}.toStringvề 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!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!