js - typeof & instanceof

439 阅读2分钟

 7种数据类型

number, string, object, boolean, function, undefined, symbol

typeof 1    // "number"
typeof '1'  // "string"
typeof true // "boolean"
typeof function() {}  // "function"
typeof []   // "object"
typeof {}   // "object"
typeof null // "object"
typeof undefined // "undefined"
typeof Symbol() // "symbol"

typeof

js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息

  • 000:对象 
  • 010:浮点数
  • 100:字符串
  • 110:布尔
  • 1:整数
  • null:所有机器码均为0
  • undefined:用 −2^30 整数来表示

所以,typeof 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待。a

typeof null历史遗留bug

在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"。

曾有一个 ECMAScript 的修复提案(通过选择性加入的方式),但被拒绝了。该提案会导致 typeof null === 'null'。

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

产生式 RelationalExpression: RelationalExpression instanceof ShiftExpression 按照下面的过程执行 :

1. 令 lref 为解释执行 RelationalExpression 的结果 .
2. 令 lval 为 GetValue(lref).
3. 令 rref 为解释执行 ShiftExpression 的结果 .
4. 令 rval 为 GetValue(rref).
5. 如果 Type(rval) 不是 Object,抛出一个 TypeError 异常 .
6. 如果 rval 没有 [[HasInstance]] 内置方法,抛出一个 TypeError 异常 .
7. 返回以参数 lval. 调用 rval 的 [[HasInstance]] 内置方法的结果

[[HasInstance]] (V)

设 F 是个函数对象。
 当以 V 作为参数调用 F 的 [[HasInstance]] 内部方法,采用如下步骤:
 
1. 如果 V 不是个对象 , 返回 false2. 令 O 为用属性名 "prototype" 调用 F 的 [[Get]] 内部方法的结果。
3. 如果 Type(O) 不是 Object, 抛出一个 TypeError 异常。
4. 重复
5. 令 V 为 V 的 [[Prototype]] 内部属性值。
6. 如果 V 是 null, 返回 false.
7. 如果 O 和 V 指向相同对象,返回 true

手写模拟

function customer_instanceof(leftValue, rightValue) {
    if ((typeof leftValue !== 'object' && typeof leftValue !== 'function') || leftValue === null) {
    return false
  }
  
  var o = rightValue.prototype;
  
  if ((typeof o !== 'object' && typeof o !== 'function') || o === null) {
    throw new TypeError('Right-hand side of customer_instanceof is not callable')
  }
  
  var v = leftValue.__proto__;
  
  while(1) {
    if (v === null || v === void(0)) return false;
    
    if (v === o) return true;
    
    v = v.__proto__;
  }
}

测试:

function Foo() {
}

console.log(Object instanceof Object) // true
console.log(Function instanceof Function)  
console.log(Function instanceof Object) // true
console.log(Foo instanceof Foo) // false
console.log(Foo instanceof Object) // true
console.log(Foo instanceof Function) // true
console.log(Object instanceof Object.prototype) // Uncaught TypeError: Right-hand side of 'instanceof' is not callable

console.log(customer_instanceof(Object, Object)) // true
console.log(customer_instanceof(Function, Function)) // true
console.log(customer_instanceof(Function, Object)) // true
console.log(customer_instanceof(Foo, Foo)) // false
console.log(customer_instanceof(Foo, Object)) // true
console.log(customer_instanceof(Foo, Function)) // true
console.log(customer_instanceof(Object, Object.prototype)) // Uncaught TypeError: Right-hand side of customer_instanceof is not callable