Javascript面向对象(十)——类检测instanceof

Javascript面向对象(十)——类检查instanceof

instanceof操作符允许监测一个对象是否属于特定的类,也考虑了继承。
很多场景需要检测,这里我们用其构建一个多态函数,处理不同参数取决他们类型。

instanceof 操作符

语法为:
obj instanceof Class

如果obj属性class(或从它继承的列)返回true,举例:
class Rabbit {}
let rabbit = new Rabbit();

// is it an object of Rabbit class?
alert( rabbit instanceof Rabbit ); // true

对构造函数一样工作:

// instead of class
function Rabbit() {}

alert( new Rabbit() instanceof Rabbit ); // true

内置的类,如Array:

let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true

请注意arr也属于Object类,Array类原型继承自Object。
instanceof操作检查原型链,使用静态方法Symbol.hasInstance,也可以实现。

obj instanceof Class的算法大概如下:

1、如果有一个静态方法Symbol.hasInstance,那么使用它,如下:

// assume anything that canEat 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

2、大多数类没有symbol.hasInstance,这种情况下,检查如果Class.prototype和obj原型链的某个原型相等。
换句话说,比较:

obj.__proto__ == Class.prototype
obj.__proto__.__proto__ == Class.prototype
obj.__proto__.__proto__.__proto__ == Class.prototype
...

在上面的示例中,Rabbit.prototype == rabbit.proto ,所以立刻返回结果。

在继承情况,rabbit也是父类的实例:

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!)

这里描绘了 rabbit instanceof Animal实现什么,和使用 Animal.prototype 比较:

顺便说下,也有一个方法objA.isPrototypeOf(objB), 如果objA是objB原型链中的某一个,则返回true。
所以使用obj instanceof Class测试,也可以使用Class.prototype.isPrototypeOf(obj) 改写。

很有趣,Class构造器不参与检查,仅原型链和Class.prototype属性检查。
所以,当prototype改变时,导致有趣的结果出现。如下:

function Rabbit() {}
let rabbit = new Rabbit();

// changed the prototype
Rabbit.prototype = {};

// ...not a rabbit any more!
alert( rabbit instanceof Rabbit ); // false

为了保障安全,所以减少改变prototype。

利好:不同类型的Object toString

我们已经知道普通对象转成字符串值为[object Object]:

let obj = {};

alert(obj); // [object Object]
alert(obj.toString()); // the same

这时调用了toString的结果。但有个隐藏特性可以让toString更强大,我们能使用其作为typeof的扩展,供instanceof的替换使用。

听起来很奇怪,确实,让我们看看。

根据规范,内置的toString可以从Object中抽取,在对象上下文中执行,返回结果依赖执行对象。

  • number 返回 [object Number]
  • boolean 返回 [object Boolean]
  • null 返回 [object Null]
  • undefined 返回 [object Undefined]
  • array 返回 [object Array]

我们开始演示:

// 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]

这里我们使用call在this=arr的上下文执行objectToString,内部toString算法检查this,并返回相应的结果。再看示例:

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

对象的toString行为可以通过特定对象属性Symbol.toStringTag进行定制。举例:

let user = {
  [Symbol.toStringTag]: 'User'
};

alert( {}.toString.call(user) ); // [object User]

对于大多数特定环境对象,有这样的属性,这里是一些浏览器相关的示例:

// toStringTag for the envinronment-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]

看到了吧,结果就是Symbol.toStringTag(如果存在),包装至[object …]。
最后,我们有更有力的typeof,不仅可以在基本数据类型上使用,也可以是内置对象,甚至可以自定义。

当我们需要对象类型字符串,而不是去检查对象类型,可以使用内置对象的toString代替instanceof。

总结

我们总结下类型检查方法:

使用对象返回值
typeof基本类型string
{}.toString基本类型,内置对象,有Symbol.toStringTag的对象string
instanceof所有对象true/false

我们看到,{}.toString技术上是更高级的typeof.
instanceof操作符在当类继承,考虑其层次结果是使用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值