instanceof和typeof的区别
在看他们的区别之前我觉得我们需要先了解两者
instanceof
来自官方的定义:
instanceof
运算符 用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上
它是可以检测到每个实例对象是否出现在构造函数的原型对象prototype
链上
所以在了解此知识前我们需要先了解原型链知识
在这篇文章中我们就不过多介绍了
所以我们先看关于instanceof
的代码例子
js
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car);
// 输出: true
console.log(auto instanceof Object);
// 输出: true
因为auto的原型链中是
js
auto
↓
Car.prototype
↓
Object.prototype
↓
null
而Car
和Object
的原型对象``prototype在auto
的原型链中所以结果为true
但是呢其实instanceof的结果也不是一成不变的
我们看以下示例
js
//定义两个函数
function C() {}
function D() {}
const o = new C(); //o-->C.prototype-->Object.prototype-->null
o instanceof C; // true
o instanceof D; // false o的原型链中没有D.prototype
o instanceof Object; // true
C.prototype instanceof Object; // true C.prototype的原型链为C.prototype-->Object.prototype-->null
//因为Object的prototype在C.prototype的原型链中 所以结果为true
C.prototype = {}; //在这里重写了C.prototype 赋予了他新的一个对象
// 原型链变为 C的新prototype--> Object.prototype-->null
const o2 = new C(); //原型链为 o2-->C的新prototype--> Object.prototype-->null
o2 instanceof C; // true 综上所述 为true
o instanceof C; //false 因为此时C的原型不在o的原型链中 因为o的原型链未被改变 其原型链中的元素还是一样 只是其中原本为C的prototype不再是 C的prtotype 但是o的原型链指向不变 还是原来的内存地址上的对象 所以在instanceof运算符执行时发现C的原型不在原型链中
D.prototype = new C();
const o3 = new D(); //其原型链为 o3 ---> D.prototype ---> new C() ---> C.prototype ---> Object.prototype ---> null
o3 instanceof D; // true
o3 instanceof C; // true
其实 instanceof 运算符就是检测目标对象的原型链上是否有某个构造函数的prototype 有就返回true
否则 false
但是构造函数的prototype不是一成不变的 所以 instanceof的值也不是一成不变的
还有也要注意目标变量是否为对象
js
var simpleStr = "This is a simple string";
var myString = new String();
var newStr = new String("String created with constructor");
simpleStr instanceof String; // 返回 false,非对象实例,因此返回 false
myString instanceof String; // 返回 true
newStr instanceof String; // 返回 true
在上面simpleStr只是一个基本类型变量不是一个实例对象
让我们来手写一个instanceof
这也是面试经常考察的点
其实中心思想就是按着实例对象的proto在原型链上一直寻找构造函数的prototype在不在上面
js
function Instanceof(instance, construction) {
let instanceval = instance.__proto__;
let Prototype = construction.prototype;
while (true) {
if (instanceval === null) return false;
if (instanceval === Prototype) return true;
instanceval = instanceval.__proto__;
}
}
console.log(Instanceof({},Object)); //输出:true
typeof
typeof
运算符返回一个字符串,表示操作数的类型。
js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息
- 000:对象
- 010:浮点数
- 100:字符串
- 110:布尔
- 1:整数
null:是 000 所以才判断为对象
js
console.log(typeof 42);
// 输出 "number"
console.log(typeof 'blubber');
// 输出 "string"
console.log(typeof true);
// 输出 "boolean"
console.log(typeof undeclaredVariable);
// 输出 "undefined"
我们先看一下能够返回的值的列表
类型 | 结果 |
---|---|
Undefined | "undefined" |
Null | "object" (原因) |
Boolean | "boolean" |
Number | "number" |
BigInt | "bigint" |
String | "string" |
Symbol | "symbol" |
Function(在 ECMA-262 中实现 [[Call]];classes也是函数) | "function" |
其他任何对象 | "object" |
我们要注意null
返回的也是object
我们再看MDN
上的例子
typeof
操作符的优先级高于加法(+
)等二进制操作符
js
// 数值
typeof 37 === "number";
typeof 3.14 === "number";
typeof 42 === "number";
typeof Math.LN2 === "number";
typeof Infinity === "number";
typeof NaN === "number"; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === "number"; // Number 会尝试把参数解析成数值
typeof Number("shoe") === "number"; // 包括不能将类型强制转换为数字的值
typeof 42n === "bigint";
// 字符串
typeof "" === "string";
typeof "bla" === "string";
typeof `template literal` === "string";
typeof "1" === "string"; // 注意内容为数字的字符串仍是字符串
typeof typeof 1 === "string"; // typeof 总是返回一个字符串
typeof String(1) === "string"; // String 将任意值转换为字符串,比 toString 更安全
// 布尔值
typeof true === "boolean";
typeof false === "boolean";
typeof Boolean(1) === "boolean"; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!1 === "boolean"; // 两次调用 !(逻辑非)运算符相当于 Boolean()
// Symbols
typeof Symbol() === "symbol";
typeof Symbol("foo") === "symbol";
typeof Symbol.iterator === "symbol";
// Undefined
typeof undefined === "undefined";
typeof declaredButUndefinedVariable === "undefined";
typeof undeclaredVariable === "undefined";
// 对象
typeof { a: 1 } === "object";
// 使用 Array.isArray 或者 Object.prototype.toString.call
// 区分数组和普通对象
typeof [1, 2, 4] === "object";
typeof new Date() === "object";
typeof /regex/ === "object";
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。 new出来的都是对象
typeof new Boolean(true) === "object";
typeof new Number(1) === "object";
typeof new String("abc") === "object";
// 函数
typeof function () {} === "function";
typeof class C {} === "function";
typeof Math.sin === "function";
你在上面也发现了typeof很难准确判断引用类型 因为都是返回object 除了函数 但是对于基础类型数据能够很好的判断
两者区别
根据上面两个介绍我相信大家都有了很好的一个了解,主要总结两者以下的特点
- typeof 会返回一个运算数的基本类型,instanceof 返回的是布尔值
- instanceof能够很好的判断引用数据的类型,但是对于基础类型数据无法判断
- typeof能够很好的判断基础数据类型,但是无法很好的判断引用类型,因为都会返回
object
- 所以两者可以说是互补,在合适的地方使用