关键词:
这是JS 原生方法原理探究系列的第五篇文章。本文会介绍如何实现 instanceof
方法。
typeof
操作符返回一个表示数据类型的字符串,它可以应付常规场景下的数据类型判断。对基本数据类型 undefined
、 boolean
、string
、 number
、Symbol
和引用数据类型 function
都可以正确判断,但是对 null、数组、对象等则统一返回 "object"。
比如说:
function F1(){}
function F2(){}
const obj1 = new F1()
const obj2 = new F2()
typeof obj1 // ‘object’
typeof obj2 // 'object'
这里只能看出 obj1
和 obj2
是对象,但不知道具体是哪个构造函数创建的对象。
但使用 instanceof
之后,就一目了然了:
console.log(obj1 instanceof F1) // true
console.log(obj1 instanceof F2) // false
console.log(obj2 instanceof F2) // true
根据 MDN 的描述:
instanceof
运算符用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上。
instanceof
运算符有两个操作数,左操作数通常是一个实例对象,它的类型可以是对象或者函数,也可以是基本类型(这种情况下不会报错,但总返回 false
),右操作数通常是一个可调用的(callable)对象,我们可以直接认为它的类型应该是一个函数。
那么 instanceof
的实现原理是什么呢?从定义中我们可以看到,它的原理和原型链的机制有关,具体地说,它会拿到右操作数的原型对象,然后在左操作数上通过 __proto__
不断查找实例的原型链,只要右操作数的 prototype
出现在左操作数的原型链上时,就返回 true。如果原型链一直查找到尽头 —— 也就是 null
,还没有找到右操作数的原型,就返回 false
。
所以,在模拟实现中,我们只要不断遍历左操作数的原型链,取得原型链上的原型对象,并与右操作数的原型对象比较即可。
下面是具体的代码实现:
function myInstanceof(instance,constructor){
if(typeof instance != 'object' && typeof instance != 'function' || instance == null){
return false
}
if(typeof constructor != 'function'){
throw TypeError('the right-hand-side of instanceof must be a function')
}
let proto = constructor.prototype
let p = instance.__proto__
while(p != null){
if(p == proto){
return true
}
p = p.__proto__
}
}
这里还可以稍微扯一下题外话。原生的 instanceof
并不支持检测基本数据类型,就和上面的实现一样,当发现左操作数是基本数据类型时,会直接返回 false。有没有办法做到让它也能检测基本数据类型呢?实际上是可以的。
根据规范的说法,在调用 instanceof
的时候,实际上会去调用内部的 @@hasInstance
方法,而这个内部方法在 ES6 中通过 [Symbol.hasInstance]
暴露出来,作为右操作数(构造函数)上的静态方法,这意味着我们可以修改这个方法,自定义 instanceof
的返回值。
举个例子,这里要检测 1 instanceof Number
,那么我们可以通过 Object.defineProperty
改写 Number
的 [Symbol.hasInstance]
方法:
Object.defineProperty(Number,Symbol.hasInstance,{
value: fucntion(x){
return typeof(x)==='object'? x instanceof Number : typeof(x) === 'number'
}
})
当调用 1 instanceof Number
的时候,实际是调用了 Number[Symbol.hasInstance](1)
,而且它既可以检测基本数据类型"number",也可以检测它的包装对象:
1 instanceof Number // true
new Number(1) instanceof Number // true
Number[Symbol.hasInstance](1) // true
Number[Symbol.hasInstance](new Number(1)) // true
如果不希望修改内置类,也可以自己实现一个 MyNumber
类:
class MyNumber{
static [Symbol.hasInstance](x){
return typeof(x)==='object'? x instanceof Number : typeof(x) === 'number'
}
}
效果是一样的:
1 instanceof MyNumber // true
new Number(1) instanceof MyNumber // true
MyNumber[Symbol.hasInstance](1) // true
MyNumber[Symbol.hasInstance](new Number(1)) // true
js原生方法原理探究如何实现浅拷贝和深拷贝?
这是JS原生方法原理探究系列的第九篇文章。本文会介绍如何手写实现浅拷贝和深拷贝。实现浅拷贝什么是浅拷贝?对原对象进行浅拷贝,会生成一个和它“一样”的新对象。但是这种拷贝只会拷贝原对象第一层的基本类型属性... 查看详情
js基础-instanceof原理及其实现
参考技术Ainstanceof的实现实际上是调用JS的内部函数[[HasInstance]]来实现的其实现原理是:只要右边变量的prototype在左边变量的原型链上即可。因此instanceof在查找过程中会遍历边变量的原型链,直到找到右边变量的prototype,如果查... 查看详情
理解javascript_07_理解instanceof实现原理
在《Javascript类型检测》一文中讲到了用instanceof来用做检测类型,让我们来回顾一下: 那么instanceof的这种行为到底是如何实现的呢,现在让我们揭开instanceof背后的迷雾。 instanceof原理照惯例,我们先来看一段代码:12345678910... 查看详情
ioscordova原生与js通讯原理
...ller,里面都有一个webview,是ios中专门显示h5页面的view.h5和原生端的交互,主要是通过插件的形式实现,原生这边写一个类继承CDVPlugin,然后实现方法,js那边封装好每个接口的方法,最终通过调用cor 查看详情
基于原生js的jsonp方法的实现
基于原生JS的jsonp方法的实现jsonp,相信大家并不陌生,是在js异步请求中解决跨域的方法之一,原理很简单,有不清楚的同学可以google下,这里就补详细解释了。在Jquery库中,jQuery直接封装有jsonp的方法,很简便,只需在ajax请求... 查看详情
ios底层原理-kvo本质探究(代码片段)
...已知类的子类NSKVONotifying_某类名,并在子类实现setter方法,set方法实现内部会顺序调用willChangeValueForKey方法、原来的setter方法实现、didChangeVal 查看详情
探究entityframework如何在多个仓储层实例之间实现工作单元的实现及原理(代码片段)
前言 1、本文的前提条件:EF上下文是线程唯一,EF版本6.1.3。 2、网上已有相关API的详细介绍,本文更多的是作为我自己的个人学习研究记录。疑问 用反编译工具翻开DbContext类可以看到EF本身就是一个实现了工作单元... 查看详情
vmwarenat模式原理探究,实现虚拟机跨网段管理
vmwarenat模式原理探究:理解nat模式,我们能更加了解主机与虚拟机之间如何通信,以及虚拟机如何实现上网。以及便于我们分析虚拟机与主机无法通信和无法上外网的问题。下面通过实战:虚拟网络拓扑,抓包分析。为什么要探... 查看详情
ios底层原理-kvo本质探究(代码片段)
...已知类的子类NSKVONotifying_某类名,并在子类实现setter方法,set方法实现内部会顺序调用willChangeValueForKey方法、原来的setter方法实现、didChangeValueForKey方法,而didChangeValueForKey方法内部又会调用监听器的observeValueForKeyPath:... 查看详情
zuul源码分析-探究原生zuul的工作原理(代码片段)
前提最近在项目中使用了SpringCloud,基于zuul搭建了一个提供加解密、鉴权等功能的网关服务。鉴于之前没怎么使用过Zuul,于是顺便仔细阅读了它的源码。实际上,zuul原来提供的功能是很单一的:通过一个统一的Servlet入口(ZuulServ... 查看详情
js中string()、newstring()探究
...字符串,str3是个对象?什么原因呢?用typeof验证一下用instanceof验证一下可以看出str3确确实实是个String对象了再来看个有意思的事,如果给String加上自定义方法和属性呢?虽然str1、str2不是对象,但可以用String上的方法和属性再... 查看详情
原生js实现一个简单的前端路由(原理)
说一下前端路由实现的简要原理,以hash形式(也可以使用HistoryAPI来处理)为例,当url的hash发生变化时,触发hashchange注册的回调,回调中去进行不同的操作,进行不同的内容的展示。直接看代码或许更直观。1functionRouter(){2this.ro... 查看详情
vue.js学习item12–内部响应式原理探究
深入响应式原理大部分的基础内容我们已经讲到了,现在讲点底层内容。Vue.js最显著的一个功能是响应系统——模型只是普通对象,修改它则更新视图。这让状态管理非常简单且直观,不过理解它的原理也很重要,可以避... 查看详情
从架构出发探究electron运行原理(代码片段)
早期桌面应用的开发主要借助原生C/C++API进行,由于需要反复经历编译过程,且无法分离界面UI与业务代码,开发调试极为不便。后期出现的QT和WPF在一定程度上解决了界面代码分离和跨平台的问题,却依然... 查看详情
浅谈instanceof和typeof的实现原理(代码片段)
typeof实现原理typeof 一般被用于判断一个变量的类型,我们可以利用 typeof 来判断number, string, object, boolean, function, undefined,symbol 这七种类型,这种判断能帮助我们搞定一些问题,比如在判断不是obj... 查看详情
js原生实现div渐入渐出
...就可以实现。fadeIn(),fadeOut();如果我们界面没有使用jq那么原生怎么实现呢?我们讲解一下,这个原理。当我们要实现渐入的时候,首先是让隐藏的div慢慢的显示,通过让opacity慢慢从0.0(完全透明)到1.0(完全不透明)。渐出就... 查看详情
hashmap实现原理探究
前言:Java8之后新增挺多新东西,在网上找了些相关资料,关于HashMap在自己被血虐之后痛定思痛决定整理一下相关知识方便自己看。图和有些内容参考的这个文章:http://www.importnew.com/16599.htmlHashMap的存储结构如图:一个桶(bucket... 查看详情
angularjs路由path方式实现原理探究
angularjs路由https://angular.io/guide/router通过URL解释,来定位客户端生成的浏览器端视图。你可绑定路由到页面的链接上,当用户点击链接,可以浏览到相应的应用视图。Thebrowserisafamiliarmodelofapplicationnavigation:EnteraURLintheaddressbarandthebro... 查看详情