Skip to content

typeof和instanceof原理

https://juejin.cn/post/6844903850395058190?searchId=202412241503366DCFD6D77D050225B1E1

1.typeof

不同的对象底层都表示为二进制,其低位的 1-3 位用来存储类型信息,typeof 就是通过判断前三位的机器码来判定类型。判定规则如下:

  • 000: 对象
  • 110: 布尔
  • 010: 浮点数
  • 100: 字符串
  • 1: 整数

有两个值比较特殊:

  • null(JSVAL_NULL):null 的所有机器码为 0,因此 typeof null 为"object"
  • undefined(JSVAL_VOID):用整数 −2^30(整数范围之外的数字)表示。

以下是 typeof 的引擎代码:

js
JS_TypeOfValue(JSContext *cx, jsval v)
{
    JSType type = JSTYPE_VOID;// 初始化为undefined
    JSObject *obj;
    JSObjectOps *ops;
    JSClass *clasp;

    CHECK_REQUEST(cx);
    if (JSVAL_IS_VOID(v)) {
        type = JSTYPE_VOID;
    } else if (JSVAL_IS_OBJECT(v)) {
        obj = JSVAL_TO_OBJECT(v);
        if (obj &&
            (ops = obj->map->ops,
             ops == &js_ObjectOps
             ? (clasp = OBJ_GET_CLASS(cx, obj),
                clasp->call || clasp == &js_FunctionClass)
             : ops->call != 0)) {
            type = JSTYPE_FUNCTION;
        } else {
            type = JSTYPE_OBJECT;
        }
    } else if (JSVAL_IS_NUMBER(v)) {
        type = JSTYPE_NUMBER;
    } else if (JSVAL_IS_STRING(v)) {
        type = JSTYPE_STRING;
    } else if (JSVAL_IS_BOOLEAN(v)) {
        type = JSTYPE_BOOLEAN;
    }
    return type;
}

可以看到 typeof 首先判断值是不是 undefined(通过值是不是等于 JSVAL_VOID(−2^30)来判断)。

c++
#define JSVAL_IS_VOID(v)  ((v) == JSVAL_VOID)

当判断为 object 类型后会作进一步判断,如果可以调用 call 或者内部属性[[Class]]标记为函数则为函数,因此 typeof 可以判断是不是函数。

c++
clasp->call
clasp == &js_FunctionClass

对于 null,通过 JSVAL_IS_OBJECT 判断为 true 后,作进一步判断,不是函数,因此为 object。

c++
#define JSVAL_IS_OBJECT(v)      (JSVAL_TAG(v) == JSVAL_OBJECT)

typeof 只能判断基本类型,此外还有一个兼容性较好的判断类型的方法,即 Objct.prototype.toString 方法,如下:

js
Object.prototype.toString.call('xhm') 		// "[object String]"
Object.prototype.toString.call(1) 			// "[object Number]"
Object.prototype.toString.call(true) 		// "[object Boolean]"
Object.prototype.toString.call(undefined) 	// "[object Undefined]"
Object.prototype.toString.call(null) 		// "[object Null]"
Object.prototype.toString.call(Symbol(1)) 	// "[object Symbol]"

Object.prototype.toString.call({ name:'xhm' })  // "[object Object]"
Object.prototype.toString.call(['a', 'b']) 		// "[object Array]"
Object.prototype.toString.call(() => {}) 		// "[object Function]"

2.instanceof

instanceof 的用途为判断对象 object 是否属于某个类型,基本用法如下:

js
/*
  object: 对象
  constructor: 构造器
  object instanceof constructor
*/

const Food = function() {};
const meat = new Food();

meat instanceof Food // true

当构造函数在执行时会将返回对象的 prototype 赋值给实例对象的__proto__,因此可以通过判断实例对象或其原型链中的__proto__是否等于构造函数的 prototype 来检查对象的类型。其实现思路大致如下:

js
const newInstanceof = (obj, ctor) => {
   let objProto = obj.__proto__;
   while(objProto) {
      if (objProto === ctor.prototype) {
        return true;
      }
      objProto = objProto.__proto__;
   }
   return false;
}
newInstanceof(meat, Food) // true

MIT Licensed