今天翻zod的源码,发现有个之前没见过的,应该也没听说的API:Symbol.hasInstance
1export /*@__NO_SIDE_EFFECTS__*/ function $constructor<T extends ZodTrait, D = T["_zod"]["def"]>( 2 name: string, 3 initializer: (inst: T, def: D) => void, 4 params?: { Parent?: typeof Class } 5): $constructor<T, D> { 6 // ... 7 // 8 Object.defineProperty(_, Symbol.hasInstance, { 9 value: (inst: any) => { 10 if (params?.Parent && inst instanceof params.Parent) return true; 11 return inst?._zod?.traits?.has(name); 12 }, 13 }); 14} 15
发现有个写了几年代码都没发现的API,就拿去问ai了。
Symbol.hasInstance是 JavaScript 的一个内置 Symbol,用于自定义 instanceof运算符的行为。
基本用法
默认情况下,instanceof检查对象的原型链:
1class Animal {} 2class Dog extends Animal {} 3 4const dog = new Dog(); 5console.log(dog instanceof Dog); // true 6console.log(dog instanceof Animal); // true 7console.log(dog instanceof Object); // true 8
使用 Symbol.hasInstance 自定义行为
你可以通过定义静态的 Symbol.hasInstance方法来改变 instanceof的行为:
1class MyClass { 2 static [Symbol.hasInstance](instance) { 3 // 自定义判断逻辑 4 return typeof instance === 'object' && 5 instance !== null && 6 instance.value === 42; 7 } 8} 9 10const obj1 = { value: 42 }; 11const obj2 = { value: 100 }; 12const obj3 = null; 13 14console.log(obj1 instanceof MyClass); // true 15console.log(obj2 instanceof MyClass); // false 16console.log(obj3 instanceof MyClass); // false 17
实际应用场景
1. 基于属性的实例检查
1class Validator { 2 static [Symbol.hasInstance](obj) { 3 return typeof obj === 'object' && 4 obj !== null && 5 typeof obj.validate === 'function'; 6 } 7} 8 9const validObj = { 10 validate() { return true; } 11}; 12 13const invalidObj = { name: "test" }; 14 15console.log(validObj instanceof Validator); // true 16console.log(invalidObj instanceof Validator); // false 17
2. 数字范围检查
1class PrimeNumber { 2 static isPrime(num) { 3 if (num <= 1) return false; 4 for (let i = 2; i <= Math.sqrt(num); i++) { 5 if (num % i === 0) return false; 6 } 7 return true; 8 } 9 10 static [Symbol.hasInstance](num) { 11 return Number.isInteger(num) && this.isPrime(num); 12 } 13} 14 15console.log(2 instanceof PrimeNumber); // true 16console.log(7 instanceof PrimeNumber); // true 17console.log(4 instanceof PrimeNumber); // false 18console.log(9 instanceof PrimeNumber); // false 19
3. 数组内容检查
1class NumericArray { 2 static [Symbol.hasInstance](arr) { 3 return Array.isArray(arr) && 4 arr.every(item => typeof item === 'number'); 5 } 6} 7 8console.log([1, 2, 3] instanceof NumericArray); // true 9console.log([1, '2', 3] instanceof NumericArray); // false 10
注意事项
- 1.必须是静态方法:
Symbol.hasInstance必须定义为类的静态方法 - 2.影响所有 instanceof 检查:会改变该类的所有实例检查行为
- 3.谨慎使用:过度使用可能使代码难以理解
1class CustomClass { 2 // 正确:静态方法 3 static [Symbol.hasInstance](instance) { 4 return true; // 所有对象都是实例 5 } 6 7 // 错误:实例方法(不会生效) 8 [Symbol.hasInstance](instance) { 9 return false; 10 } 11} 12 13console.log({} instanceof CustomClass); // true(总是返回 true) 14
总结
Symbol.hasInstance的主要用途是让开发者能够自定义 instanceof运算符的逻辑,实现更灵活的类型检查机制。这在创建验证器、特殊数据类型的类或者需要复杂实例检查的场景中非常有用。
为什么这样设计
- 灵活性:允许对象通过特征标记而非严格的类继承来表示类型
- 组合优于继承:支持特征组合,一个对象可以有多个特征
- 运行时类型检查:可以在运行时动态添加/移除类型特征
这种模式在需要灵活类型系统的库中很常见,特别是那些需要支持动态类型或混合类型的场景。
