JS基础之instanceof

instanceof

在JS中,判断一个变量的类型,常常会用到typeof运算符,但当用来判断引用类型变量时,无论是什么类型的变量,它都会返回Object。为此,引入了instanceof

instanceof相比与typeof来说,instanceof方法要求开发者明确的确认对象为某特定类型。即instanceof用于判断引用类型属于哪个构造函数的方法。

var arr = []
arr instanceof Array // true
typeof arr // object, typeof 是无法判断是否为数组的

另外,更重的一点是 instanceof 可以在继承关系中用来判断一个实例是否属于它的父类型。

// 判断 f 是否是 Foo 类的实例 , 并且是否是其父类型的实例
function Aoo(){} 
function Foo(){} 
Foo.prototype = new Aoo();//JavaScript 原型继承
 
var f = new Foo(); 
console.log(foo instanceof Foo)//true 
console.log(foo instanceof Aoo)//true

f instanceof Foo 的判断逻辑是:

  • f 的 __proto__一层一层往上,是否对应到 Foo.prototype
  • 再往上,看是否对应着Aoo.prototype
  • 再试着判断 f instanceof Object

instanceof可以用于判断多层继承关系。

下面看一组复杂例子

console.log(Object instanceof Object) //true 
console.log(Function instanceof Function) //true 
console.log(Number instanceof Number) //false 
console.log(String instanceof String) //false 
console.log(Array instanceof Array) // false
 
console.log(Function instanceof Object) //true 
 
console.log(Foo instanceof Function) //true 
console.log(Foo instanceof Foo) //false

在这组数据中,Object、Function instanceof 自己为true, 其他的instanceof自己都为false,这就要从instanceof的内部实现机制以及JS原型继承机制讲起。

1. instanceof的内部实现机制

instanceof的内部实现机制是通过判断对象的原型链上是否能找到对象的 prototype

// instanceof 的内部实现 
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式,即L为变量,R为类型
 // 取 R 的显示原型
 var prototype = R.prototype
 // 取 L 的隐式原型
 L = L.__proto__
 // 判断对象(L)的类型是否严格等于类型(R)的显式原型
 while (true) { 
   if (L === null) 
     return false
   if (prototype === L)// 这里重点:当 prototype 严格等于 L 时,返回 true 
     return true
   L = L.__proto__
 } 
}

2. JS原型链继承关系

在这里插入图片描述

图片来自于JS原型链
由其本文涉及显示原型和隐式原型,所以下面对这两个概念作一下简单说明。在 JavaScript 原型继承结构里面,规范中用 [[Prototype]] 表示对象隐式的原型,在 JavaScript 中用 __proto__ 表示,并且在 Firefox 和 Chrome 浏览器中是可以访问得到这个属性的,但是 IE 下不行。所有 JavaScript 对象都有 proto 属性,但只有 Object.prototype.__proto__ 为 null,前提是没有在 Firefox 或者 Chrome 下修改过这个属性。这个属性指向它的原型对象。 至于显示的原型,在 JavaScript 里用 prototype 属性表示,这个是 JavaScript 原型继承的基础知识,如果想进一步了解,请参考JS基础之原型与原型链

下面介绍几个例子,加深你的理解:

  • Object instanceof Object

    // 为了方便表述,首先区分左侧表达式和右侧表达式
    ObjectL = Object, ObjectR = Object; 
    // 下面根据规范逐步推演
    O = ObjectR.prototype = Object.prototype 
    L = ObjectL.__proto__ = Function.prototype 
    // 第一次判断
    O != L 
    // 循环查找 L 是否还有 __proto__ 
    L = Function.prototype.__proto__ = Object.prototype 
    // 第二次判断
    O === L 
    // 返回 true
    
  • Function instanceof Function

    // 为了方便表述,首先区分左侧表达式和右侧表达式
    FunctionL = Function, FunctionR = Function; 
    // 下面根据规范逐步推演
    O = FunctionR.prototype = Function.prototype 
    L = FunctionL.__proto__ = Function.prototype 
    // 第一次判断
    O === L 
    // 返回 true
    
  • Foo instanceof Foo

    // 为了方便表述,首先区分左侧表达式和右侧表达式
    FooL = Foo, FooR = Foo; 
    // 下面根据规范逐步推演
    O = FooR.prototype = Foo.prototype 
    L = FooL.__proto__ = Function.prototype 
    // 第一次判断
    O != L 
    // 循环再次查找 L 是否还有 __proto__ 
    L = Function.prototype.__proto__ = Object.prototype 
    // 第二次判断
    O != L 
    // 再次循环查找 L 是否还有 __proto__ 
    L = Object.prototype.__proto__ = null 
    // 第三次判断
    L == null 
    // 返回 false
    

3. instanceof和多全局对象(多个frame或多个window之间的交互)

instanceof在多个全局作用域下,判断会有问题,例如:

//  parent.html
	<iframe src="child.html" onload="test()">
	</iframe>
    <script>
        function test(){
            var value = window.frames[0].v;
            console.log(value instanceof Array); // false
        }
    </script>
//  child.html
    <script>
        window.name = 'child';
        var v = [];
    </script>

严格上来说value 就是数组,但parent页面中打印输出: false ;这是因为 Array.prototype !== window.frames[0].Array.prototype,并且数组从前者继承。
出现问题主要是在浏览器中,当我们的脚本开始开始处理多个frame或windows 或在多个窗口之间进行交互。多个窗口意味着多个全局环境,不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。
解决方法:可以通过使用 Array.isArray(myObj) 或者Object.prototype.toString.call(myObj) === "[object Array]"来安全的检测传过来的对象是否是一个数组

扩展:Object.prototype.toString 方法

默认情况下(不覆盖 toString 方法前提下),任何一个对象调用 Object 原生的 toString 方法都会返回 “[object type]”,其中 type 是对象的类型;
每个类的内部都有一个 [[Class]] 属性,这个属性中就指定了上述字符串中的 type(构造函数名) ;

function isFunction(value) {
        return Object.prototype.toString.call(value) === "[object Function]"
    }
function isDate(value) {
        return Object.prototype.toString.call(value) === "[object Date]"
    }
function isRegExp(value) {
        return Object.prototype.toString.call(value) === "[object RegExp]"
    }

isDate(new Date()); // true
isRegExp(/\w/);        // true
isFunction(function(){}); //true

或者可写为:

 function generator(type){
        return function(value){
            return Object.prototype.toString.call(value) === "[object "+ type +"]"
        }
    }

 let isFunction = generator('Function')
 let isArray = generator('Array');
 let isDate = generator('Date');
 let isRegExp = generator('RegExp');

isArray([]));    // true
isDate(new Date()); // true
isRegExp(/\w/);        // true
isFunction(function(){}); //true

本文参考JavaScript instanceof 运算符深入剖析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值