浅析instanceof 原理

1,264 阅读5分钟

概述

js中可以通过typeof操作符和instanceof操作符判断变量的类型。而typeof在判断基本类型的变量时能精准判断。当变量是数组、对象时都只会返回‘object',无法准确的说明该对象是什么类型的。但是instanceof基于原型链可以判断一个引用是否属于某构造函数。能弥补typeof 方法判断变量类型的缺点。本文旨在分析instanceof的底层原理,同时借此复习原型以及原型链的知识。

一、js中变量类型

本节先来回顾一下js的变量类型,js变量可以分为基本类型和引用类型。

(1)基本类型:字符串(string)、数值(number)、布尔值(boolean)、undefined、Null、Symbol

(2)引用类型:对象(Object)

基本类型和引用类型的区别

基本类型:

1、值类型的变量值存储在栈中,而引用类型的变量值 存储在栈中的 是指向堆中数组或对象的地址,这就是为何修改引用类型总会影响到其他指向这个地址的引用变量。)

2、保存与复制的是值本身

3、使用typeof检测数据的类型

引用类型:

1、保存在堆中

2、保存与复制的是指向对象的一个指针

3、使用instanceof检测数据类型

4、使用new()方法构造出的对象是引用型

在实际开发过程中,我们有时候需要判断变量的类型去做不同的逻辑处理。就会使用到typeof和instanceo操作符来判断变量类型。下面来看一下这个两个方法的区别。

二、typeof 和instanceof判断变量类型

(1)typeof 判断变量类型

typeof操作符是确定一个变量是字符串、数值、布尔值、还是undefined的最佳工具。如果一个变量是一个对象或者null,那么typeof就会返回“object”;虽然typeof在检测基本数据类型时是非常的得力的助手,但是在检测对象类型的时候用处就不大了,此时我们就需要instanceof操作符了,语法如下:

result = variable instanceof constructor

如果变量是给定的引用类型那么就会返回true,否则就会返回false

所有的引用类型的值都是Object的实例,因此在检测一个引用类型值和Object的构造函数时,instanceof都会返回true,如果使用instanceof操作符检测基本类型的值,则该操作符时钟会返回false,因此基本类型不是对象。那么instanceof的原理是什么?为什么能判断 变量是否来源于某个构造函数?涉及到原型以及原型链的知识。下一节研究什么是原型以及原型链。搞懂原型和原型链后instanceof的原理就明白了。

三、原型和原型链

1、原型

原型分为隐式原型(__proto__)和显式原型(prototype)。 由于所有的引用类型(数组、对象、函数)都具有对象特性。即可自由扩展属性(除了‘null')。因此浏览器为每一个可扩展的对象添加了一个__proto__属性。该属性称为隐式原型,是一个普通对象。

可以看到__proto__是一个对象,具有constructor、hansOwnProperty等属性。 函数也是引用类型,因此浏览器为其扩展了一个默认属性 prototype。叫作显式原型。也是一个普通的对象。

该对象包含了constructor和__proto__属性。隐式原型和显示原型的关系用一句话概述就是:对象的隐式原型指向构造该对象的构造函数的显式原型。

对象student是通过构造函数People实例化而来的。因此student的隐式原型(proto)与构造函数People的显示原型(prototype)完全相等。

2、原型链

当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的隐式原型__proto__(即它的构造函数的prototype)中寻找。如果它的构造函数的prototype中没有该属性,那么就会通过prototype.__proto__中去寻找。以此类推如果存在该属性会直到找到该属性为止。浏览器为了防止无限循环,找到最上层就是object了(祖先),再往上找就是null,说明此时不存在该属性。这个寻找的过程会形成一条链路,就是原型链。如下图所示:

其中Foo是构造函数,f是其实例化对象。曲线__proto__形成的就是原型链。 3、原型链的应用 简单模拟jquery封装dom方法的过程。

div1是构造函数Elem的实例化对象,调用html(div1.html())方法时,首先会在div1上寻找该方法。但是该对象上并没有该方法,因此会通过div1.__proto__即Elem.prototype去寻找该方法。正好Elem.prototype上有html方法,停止寻找并执行该方法。当调用toString()方法时候还是通过同样的方式去寻找,但是Elem.prototype上也不存在该方法,于是就通过Elem.prototype.__proto__向上寻找。所有对象的构造函数是Object。所以此时会去Object.prototype上寻找toString()方法。正好该对象上存在该方法,于是停止寻找并执行该方法。

四、instanceof 判断对象类型原理

上面分析了原型以及原型链的知识,现在我们看看instanceof底层是如何工作的。

instanceof底层实现的核心就是去判断对象的隐式原型是否等于构造函数的显式原型。如果相等就返回true。比如test是构造函数Test的实例化对象。因此会test.proto === Test.prototype因此会返回true,证明test是Test类型。而test.proto !== Object.prototype。因此会执行else逻辑递归判断。此时instance传入的参数为Test.prototype和Object。此时Test.prototype.proto === Object.prototype。因此返回true。证明test也是Object类型。 因此instanceof 是从原型的角度,来判断某引用属于哪个构造函数,从而判定它的数据类型。这样就弥补了typeof判断引用类型不准确的缺点。

五、总结

本文通过分析原型以及原型链,探索了instanceof的核心思想。原型以及原型链是js中很重要的知识点,也是自己一直以来不清晰的点。通过这次总结豁然开朗。文章中分析不到位之处望读者补充指正。