typeof和instanceof实现原理

typeof

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

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

有两个值比较特殊:bash

  • null(JSVAL_NULL)markdown

    null 的全部机器码为 0,所以 typeof null 为"object"函数

  • undefined(JSVAL_VOID)oop

    用整数 −2^30(整数范围以外的数字)表示。post

如下是 typeof 的引擎代码spa

JS_PUBLIC_API(JSType)
   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)来判断)。prototype

#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID)
复制代码

当判断为 object 类型后会做进一步判断,若是能够调用 call 或者内部属性[[Class]]标记为函数则为函数,所以 typeof 能够判断是否是函数。code

clasp->call
clasp == &js_FunctionClass
复制代码

对于 null,经过 JSVAL_IS_OBJECT 判断为 true 后,做进一步判断,不是函数,所以为 object。orm

#define JSVAL_IS_OBJECT(v) (JSVAL_TAG(v) == JSVAL_OBJECT)
复制代码

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

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]"
复制代码

instanceof

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

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

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

meat instanceof Food // true
复制代码

JS 中的继承与原型链一文咱们能够知道,当构造函数在执行时会将返回对象的 prototype 赋值给实例对象的__proto__,所以能够经过判断实例对象或其原型链中的__proto__是否等于构造函数的 prototype 来检查对象的类型。其实现思路大体以下:

var newInstanceof = (obj, ctor) => {
   let objProto = obj.__proto__;
   while(objProto) {
      if (objProto === ctor.prototype) {
        return true;
      }
      objProto = objProto.__proto__;
   }
   return false;
}
newInstanceof(meat, Food) // true
复制代码

来看下面的例子:

var Food = function() {};
var Meat = function() {};
Meat.prototype = new Food();
var meat = new Meat();

newInstanceof(meat, Meat) // true
newInstanceof(meat, Food) // true
meat instanceof Meat // true
meat instanceof Food // true
复制代码

咱们看到 meat instanceof Food 为 true,由于 meat 在原型链上可以找到 Food,来看另外一个例子:

var Meat = function() {
  return { name: 'xhm' };
};
var meat = new Meat();
newInstanceof(meat, Meat); // false
meat instanceof Meat // false
复制代码

咱们看到 meat instanceof Meat 为 false,由于 instanceof 的本质是判断原型链上的对象,而当一个对象不是经过原型构造出来的实例时(Meat 构造函数返回了一个与 Meat 绝不相干的对象),这种断定方法就会失效。

相关文章
相关标签/搜索