Skip to content

JS-操作符-instanceof #190

Open
Open
@yaofly2012

Description

@yaofly2012

一、语法

1.1 基本用法

object instanceof constructorFunc

判断构造函数constructorFuncprototype属性是否在对象object的原型链上。

Object.create({}) instanceof Object // true
Object.create(null) instanceof Object // false

Function instanceof Object // true
Function instanceof Function // true
Object instanceof Object // true

1. 作为类型判断的一种方式,instanceof 操作符不会对object进行隐式类型转换

"" instanceof String; // false,基本类型不会转成对象
new String('') instanceof String; // true
  • 对于没有原型的对象,或则基本类型直接返回false
1 instanceof Object // false
Object.create(null) instanceof Object // false

2. constructorFunc必须是函数,且可以作为构造函数(即要具有prototype属性)

// TypeError: Right-hand side of 'instanceof' is not callable
1 instanceof ({})

// TypeError: Function has non-object prototype 'undefined' in instanceof check
({}) instanceof (() => {})

3. 对于跨全局执行上下文intanceof就不能正常工作了

不同的全局执行上下文的对象和函数都是不相等的。

<!DOCTYPE html>
<html>
    <head></head>
    <body>
        <iframe src=""></iframe>
        <script type="text/javascript">
            var iframe = window.frames[0];
            var iframeArr = new iframe.Array();

            console.log([] instanceof iframe.Array) // false
            console.log(iframeArr instanceof Array)  // false
            console.log(iframeArr instanceof iframe.Array)  // true       
        </script>
    </body>
</html>

1.2 PK Object.prototype.isPrototypeOf()

Object.prototype.isPrototypeOf()用于判断对象是否在另一个对象的原型链上。
[] instanceof Array相等于Array.prototype.isPrototypeOf([])
看似可以利用Object.prototype.isPrototypeOf()代替instanceof,但是其实不是的见下面“自定义instanceof行为”。

综上:只是instanceof内部逻辑利(OrdinaryHasInstance)和Object.prototype.isPrototypeOf()存在相似的地方。

二、instanceof check

var foo = () => {}
var a = {}

// TypeError: Function has non-object prototype 'undefined' in instanceof check
a instanceof foo

// 显示增加个`prototype `成员函数(必须是函数)就不报错了
foo.prototype = function() {}
a instanceof foo // false

要弄清原因还是看下规范InstanceofOperator

  1. If Type(target) is not Object, throw a TypeError exception.
  2. Let instOfHandler be ? GetMethod(target, @@hasInstance).
  3. If instOfHandler is not undefined, then
  • Return ! ToBoolean(? Call(instOfHandler, target, « V »)).
  1. If IsCallable(target) is false, throw a TypeError exception.
  2. Return ? OrdinaryHasInstance(target, V).

即:

  1. 如果constructorFunc不是对象,直接抛TypeError异常;
  2. 如果constructorFunc定义了Symbol.hasInstance方法(不为undefined就算),则调用该方法,并以该方法的返回值作为instanceof的值;
var foo = () => {}
var a = {}

// 显示增加个`prototype `成员函数(必须是函数)就不报错了
foo.prototype = function() {}

// 自定义Symbol.hasInstance方法
Object.defineProperty(foo, Symbol.hasInstance, {
  value: {} // 不是个方法
});

// TypeError: object is not a function
a instanceof foo
  1. 否则走OrdinaryHasInstance(target, V)流程。

三、自定义instanceof行为

instanceof操作符默认通过原型链判断(OrdinaryHasInstance),但可以利用Symbol.hasInstance自定义instanceof行为。

var foo = () => {}
var a = {}

// 显示增加个`prototype `成员函数(必须是函数)就不报错了
foo.prototype = function() {}

// 自定义Symbol.hasInstance方法
Object.defineProperty(foo, Symbol.hasInstance, {
  value: function(instance) { 
    return true
   }
});

a instanceof foo // true

所有函数(包含不可以作为构造函数的函数,如Function.prototype)都定义了Symbol.hasInstance方法。

function foo() {
}

typeof  foo[Symbol.hasInstance]() // "function"
typeof Function.prototype[Symbol.hasInstance] // "function"

四、利用Object.getPrototypeOf实现instanceof

function isInstanceOf(obj, constructor) {
    if(typeof constructor !== 'object') {
        throw new TypeError('constructor is not a object')
    }

    // Symbol.hasInstance方式
    if(constructor[Symbol.hasInstance] !== undefined) {
        return !!constructor[Symbol.hasInstance](obj);
    }

    // 原型链方式:
    if(typeof obj !== 'object') {
        return false
    }

    if(typeof constructor !== 'function') {
        throw new TypeError('constructor is not a function')
    }

    if(typeof constructor.prototype !== 'object') {
        throw new TypeError('Function has non-object prototype');
    }

    while(obj !== null) {
        if(obj === constructor.prototype) {
            return true;
        }

        obj = Object.getPrototypeOf(obj);
    }

    return false;
}

参考

  1. MDN instanceof
  2. JavaScript instanceof 运算符深入剖析

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions