summaries[类检查:“instanceof” ]

总结:类型检查方法

| 用于 | 返回值 |
|-----------------------------------------------------------------------|-------------------------------------------|------------|
| typeof | 原始数据类型 | string |
| {}.toString | 原始数据类型,内建对象,包含 Symbol.toStringTag 属性的对象 | string |
| instanceof | 对象 | true/false |
| 使用类的层次结构(hierarchy),并想要对该类进行检查,同时还要考虑继承时,这种场景下 instanceof 操作符确实很出色。 | |


instanceof 操作符用于检查一个对象是否属于某个特定的 class。同时,它还考虑了继承。 instanceof 在检查中会将原型链考虑在内, 还可以在静态方法 Symbol.hasInstance 中设置自定义逻辑。


在 JavaScript 中,Symbol.hasInstance 是一个特殊的内置方法。 所有的函数(在 JavaScript 中,类就是特殊的函数)默认都会包含这个方法它决定了 instanceof 操作符的行为。 默认情况下,如果您有一个对象 o 和一个类(也就是函数) C,那么表达式 o instanceof C 会检查 o 的原型链是否包含 C.prototype,如果包含,就返回 true,否则返回 false。所以默认情况下,instanceof 是检查一个对象是否是一个类的实例。

Symbol.hasInstance 方法提供了一个方式,允许我们自定义 instanceof 的行为。具体来说,就是我们可以在一个类上定义一个标记为 Symbol.hasInstance 的方法,这个方法接受一个参数,参数就是 instanceof 左侧的对象,然后返回一个布尔值个表示这个对象是否应该被视为这个类的实例。

js 复制代码
class MyArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
};
console.log([] instanceof MyArray); // 输出:true
console.log([1, 2, 3] instanceof MyArray); // 输出:true
console.log({} instanceof MyArray); // 输出:false
  1. MyArray类定义了静态方法[Symbol.hasInstance],用于自定义instanceof。
  2. 当执行[] instanceof MyArray时,空数组[]被传入了[Symbol.hasInstance]的instance参数。
  3. 在方法内部,对instance调用了Array.isArray进行判断。
  4. 空数组[]确实是一个数组,所以Array.isArray返回true。
  5. 因此静态方法也返回true,该表达式最终结果为true。
  6. 判断过程充分利用了[Symbol.hasInstance]和instance参数,自定义了instanceof的逻辑。 (1)问题[]instanceof MyArray会自动调用静态方法?
  • 当使用 instanceof 操作符时,语言内部会自动检查被操作对象的类型,以确定 instanceof 的行为。 具体来看,对于 [] instanceof MyArray 这一行
  1. []是一个空数组,它的类型为 Array。
  2. MyArray 是一个类。
  3. 语言内部发现 MyArray 是一个函数/类。
  4. 语言内部会自动检查 MyArray 是否有定义 Symbol.hasInstance 方法。
  5. 发现 MyArray 确实定义了该方法。
  6. 所以语言内部会自动调用这个方法,以定制 instanceof 的执行逻辑。
  7. 方法的返回值会成为 instanceof 表达式的结果。 参考词法环境分析: 当 JavaScript 解析器处理这样一个表达式时:
  8. 首先会看到 [] 是一个数组字面量,分析为一个数组对象。
  9. 然后看到 instanceof 是一个二元运算符。
  10. 根据运算符语法,它的右操作数通常是一个类构造函数。
  11. 这里右操作数是 MyArray,解析器会分析这是一个类。
  12. 对于类,根据 instanceof 运算符的==语言规范==,如果类定义了 Symbol.hasInstance 方法,则会自动调用该方法。
  13. 这里 MyArray 类确实定义了 Symbol.hasInstance
  14. 因此根据实例运算符的==词法环境==,解析器会自动调用这个方法。 自动调用的根本原因是:
  • instanceof 语法规定了这种自动调用行为
  • 解析器会分析这种语法结构并调用 (2)为什么所有函数默认都有[Symbol.hasInstance] 方法?
js 复制代码
// 默认的函数和类行为
function Foo() {} 

// 内部默认包含这个静态方法  
Foo[Symbol.hasInstance] = function(instance) {
  return instance instanceof Foo; 
}

这个默认行为是在语言规范层面定义的:

  • 函数和类会自动包含这个静态方法
  • 确保自定义 instanceof 逻辑可用 (3)是否可以重写所有函数默认[Symbol.hasInstance] 方法? ------参考[[3.静态属性和静态方法]]
js 复制代码
class MyClass {
  static [Symbol.hasInstance](obj) {
    // 自定义逻辑
    return true;
  }
}
let obj = {};
obj instanceof MyClass; // true

[Symbol.hasInstance] 的重写步骤是:

  1. 在类上定义[Symbol.hasInstance]静态方法
  2. 方法参数为要检查的对象
  3. 方法内部编写自定义判断逻辑
  4. 返回 true 或 false
  5. 该方法会在 instanceof 时被调用 适用场景:
  • 需要调整 instanceof 的判断规则时
  • 当默认的原型链检查不足以判断实例时
  • 想要 instanceof 在特定类上具有不同的语义时

原文资料

instanceof 操作符

js 复制代码
// test 类
class Rabbit {}
let rabbit = new Rabbit();
// rabbit 是 Rabbit class 的对象吗?
alert( rabbit instanceof Rabbit ); // true
// 检验是否为实例

语法:

  • 1.B instanceof A,B is A's instance ? true : false
    1. 在静态方法 Symbol.hasInstance 中==设置自定义逻辑。==
js 复制代码
// test 构成函数
// 声明一个构造函数 Rabbit
function Rabbit() {} 

// 使用 new 关键字创建 Rabbit 的一个实例
let rabbit = new Rabbit();

// instanceof 运算符检查 rabbit 是否是 Rabbit 的实例
// 如果是,返回 true。不是则返回 false
alert( rabbit instanceof Rabbit ); // true
instanceof Object
js 复制代码
// test 原生
let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
// 因为在 JavaScript 中,Array 是继承自 Object 的
// 所以 instanceof Object 对任何数组、对象、函数等都会返回 true。
// instanceof 检查整个原型链,只要出现在实例的原型链上,就会返回 true。

静态方法 Symbol.hasInstance,那就直接调用这个方法:

js 复制代码
// 设置 instanceOf 检查
// 并假设具有 canEat 属性的都是 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) 被调用

第一部分

js 复制代码
class Animal {
  static [Symbol.hasInstance](obj) {
    if (obj.canEat) return true;
  }
}
  1. static Symbol.hasInstance]{...} 是静态方法
  2. Symbol.hasInstance 是一个内置的 Symbol 值
  3. Symbol.hasInstance - 一个内置Symbol值
  4. [Symbol.hasInstance] 将这个 Symbol 值作为方法名,定义实例判断逻辑
  5. Symbol.hasInstance用来自定义instanceof运算符的行为 第二部分
js 复制代码
let obj = { canEat: true };
alert(obj instanceof Animal); 

当执行alert(obj instanceof Animal); obj这个对象会被传入[Symbol.hasInstance]方法的obj参数 最终返回true

涉及:数据类型Symbol
js 复制代码
let mySymbol = Symbol();
// 第一种写法,属性方括号操作表示法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

注意,Symbol 值作为对象属性名时,不能用点运算符。

js 复制代码
// 示例
const mySymbol = Symbol();
const a = {};
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"

内置的 Symbol 值: Symbol.hasInstance 对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。

js 复制代码
class MyClass {
  [Symbol.hasInstance](foo) {
    return foo instanceof Array;
  }
}
[1, 2, 3] instanceof new MyClass() // true

另一个例子:

js 复制代码
class Even {
  static [Symbol.hasInstance](obj) {
    return Number(obj) % 2 === 0;
  }
}

// 等同于
const Even = {
  [Symbol.hasInstance](obj) {
    return Number(obj) % 2 === 0;
  }
};

1 instanceof Even // false
2 instanceof Even // true
12345 instanceof Even // false

Symbol.hasInstance是一个内置的Symbol值,它的作用是自定义instanceof运算符的行为。 使用方式是:

  1. 在类或对象上定义[Symbol.hasInstance]方法
  2. 当其他对象使用instanceof时,会自动调用这个方法
  3. 在方法内部可以自定义判断逻辑,返回true或false
  4. 这样就实现了自定义的instanceof行为

objA.prototype.isPrototypeOf(objB)

isPrototypeOf() 方法用于检查一个对象是否存在于另一个对象的原型链中。 理解为:"objA对象是否是objB的原型" ------【isPrototypeOf()

js 复制代码
class Animal {}
class Rabbit extends Animal {}
let rabbit = new Rabbit();
alert(rabbit instanceof Animal); // true

Animal.prototype.isPrototypeOf(rabbit); // true
Rabbit.prototype.isPrototypeOf(rabbit); // true

// 对比
// 语句1;
Animal.isPrototypeOf(Rabbit); // true
// 语句2;
Animal.prototype.isPrototypeOf(Rabbit.prototype); // true

JavaScript的原型继承机制来分析 为什么Animal.prototype.isPrototypeOf(Rabbit)会返回false?

  1. Rabbit 通过 extends 关键字继承自 Animal。
  2. ==在继承时,会将 Animal.prototype 赋值给 Rabbit.prototype。==
  3. 所以 Rabbit.prototype 的原型是 Animal.prototype。
  4. 但是 Rabbit 自身和 Animal.prototype 没有直接的原型关系。
  5. Rabbit 只是 Animal 的一个子类/实例。 构造函数示例
js 复制代码
function Animal() {}
function Rabbit() {}
Rabbit.__proto__ = Animal; 
Rabbit.prototype = Object.create(Animal.prototype);
Animal.isPrototypeOf(Rabbit); // true  
Animal.prototype.isPrototypeOf(Rabbit.prototype); // true

Rabbit.__proto__ = Animal; 改为Rabbit.__proto__ = Animal.prototype;

js 复制代码
// 一样的
Animal.isPrototypeOf(Rabbit); // true  
Animal.prototype.isPrototypeOf(Rabbit); // true

分化讨论:

js 复制代码
Rabbit.__proto__ = Animal; // 继承静态成员
Rabbit.prototype.__proto__ = Animal.prototype; // 继承实例成员

改为:

js 复制代码
Rabbit.__proto__ = Animal.prototype;

结果:

  • Animal.isPrototypeOf(Rabbit) -> true
  • Animal.prototype.isPrototypeOf(Rabbit) -> true
  • Animal.prototype.isPrototypeOf(Rabbit.prototype) -> true 原因:
  1. 现在Rabbit函数的原型直接设置为了Animal.prototype,跳过了Animal函数。
  2. Animal.prototype的构造函数constructor属性指向Animal函数。
  3. 所以Rabbit的原型链上依然存在Animal函数。
  4. isPrototypeOf按原型链查找,只要链上存在即可。 所以修改后,Rabbit函数仍然通过原型链继承自Animal函数。 isPrototypeOf检查原型链,所以结果不变。

使用 Object.prototype.toString 方法来揭示类型

js 复制代码
// 方便起见,将 toString 方法复制到一个变量中
let objectToString = Object.prototype.toString;
// 它是什么类型的?
let arr = [];
alert( objectToString.call(arr) ); // [object Array]

装饰器模式和转发,call/apply 一章中讲过的 call 方法来在上下文 this=arr 中执行函数 objectToString。

js 复制代码
let s = Object.prototype.toString;
alert( s.call(123) ); // [object Number]
alert( s.call(null) ); // [object Null]
alert( s.call(alert) ); // [object Function]

Symbol.toStringTag

使用特殊的对象属性 Symbol.toStringTag 自定义对象的 toString 方法的行为。

js 复制代码
let user = {
  [Symbol.toStringTag]: "User"
};
alert( {}.toString.call(user) ); // [object User]
相关推荐
理想不理想v4 分钟前
使用JS实现文件流转换excel?
java·前端·javascript·css·vue.js·spring·面试
EasyNTS25 分钟前
无插件H5播放器EasyPlayer.js网页web无插件播放器vue和react详细介绍
前端·javascript·vue.js
老码沉思录29 分钟前
React Native 全栈开发实战班 - 数据管理与状态之Zustand应用
javascript·react native·react.js
老码沉思录34 分钟前
React Native 全栈开发实战班 :数据管理与状态之React Hooks 基础
javascript·react native·react.js
guokanglun1 小时前
Vue.js动态组件使用
前端·javascript·vue.js
我认不到你1 小时前
antd proFromSelect 懒加载+模糊查询
前端·javascript·react.js·typescript
scc21401 小时前
spark的学习-06
javascript·学习·spark
我是苏苏2 小时前
C# Main函数中调用异步方法
前端·javascript·c#
转角羊儿2 小时前
uni-app文章列表制作⑧
前端·javascript·uni-app
Bio Coder3 小时前
学习用 Javascript、HTML、CSS 以及 Node.js 开发一个 uTools 插件,学习计划及其周期
javascript·学习·html·开发·utools