JS魔法堂:再识instanceof
一、Breif
大家都知道instanceof一般就是用来检查A对象是否为B类或子类的实例。那问题是JS中没有类的概念更没有类继承的概念(虽然有构造函数),那么instanceof到底是怎样判断A对象是B构造函数的实例呢?本文将对此作分析记录,以便日后查阅。
二、Reference 2 ECMA-262-3 Spec
http://bclary.com/2004/11/07/#a-11.8.6
The production RelationalExpression: RelationalExpression instanceof ShiftExpression is evaluated as follows:
1. Evaluate RelationalExpression.
2.Call GetValue(Result(1)).
3.Evaluate ShiftExpression.
4.Call GetValue(Result(3)).
5.If Result(4) is not an object, throw a TypeError exception.
6.If Result(4) does not have a [[HasInstance]] method, throw a TypeError exception.
7.Call the [[HasInstance]] method of Result(4) with parameter Result(2).
8.Return Result(7).
从上述的定义我们可以得出以下内容:
1. ShiftExpression的实际值(GetValue(Evaluate(ShiftExpression)))必须为[object Function],否则就抛TypeError异常;
2. instanceof的实际判断则是调用RelationalExpression的Internal Method [[HasInstance]]来处理。
下面我们深入一下[[HasInstance]]的定义
http://bclary.com/2004/11/07/#a-15.3.5.3
Assume F is a Function object.
When the [[HasInstance]] method of F is called with value V, the following steps are taken:
1. If V is not an object, return false.
2. Call the [[Get]] method of F with property name "prototype".
3. Let O be Result(2).
4. If O is not an object, throw a TypeError exception.
5. Let V be the value of the [[Prototype]] property of V.
6. If V is null, return false.
7. If O and V refer to the same object or if they refer to objects joined to each other (13.1.2), return true.
8. Go to step 5.
上面的定义看得不太明白,我们把它翻译成JS的实现吧
// IE5.5~9下,由于无法通过__proto__访问对象的Internal Property [[Prototype]],因此该方法无效
;(function(rNotObj){
Function.prototype['[[HasInstance]]'] = function(value){
// 1. If V is not an object, return false
if (rNotObj.test(typeof value)) return false
// 2. Call the [[Get]] method of F with property name "prototype"
// 4. If O is not an object, throw a TypeError exception
var O = this.prototype
if (rNotObj.test(typeof O)) throw TypeError() // 5. Let V be the value of the [[Prototype]] prototype of V
// 6. If V is null, return false
if (null === (value = value.__proto__)) return false // 7. If O and V refer to the same object
// 8. Go to step 5
return O === value || this['[[HasInstance]]'](value)
}
}(/$[^of]/ /*not begin with o(bject) neither f(unction)*/))
现在稍微总结一下,a instanceof b底层的运算机制关键点如下:
1. b的数据类型必须为[object Function],否则就抛TypeError;
2. 若a为Primitive Value则直接返回false, 若a的数据类型为Object则执行后续运算;
3. 当且仅当b.prototype位于a的prototype chain中时,才返回true(由于Object.prototype.__proto__为null,因此prototype chain是有限链表);
也许大家会对 Function.prototype['[[HasInstance]]'] 的实现为什么能成功感到疑问,我们先看看以下图片

可以知道所有函数的 __proto__ 默认情况下均指向 Function.prototype ,而 Function.__proto__ 则与 Function.prototype 指向同一个对象。
Chrome中两者均指向 function Empty(){} ,因此添加到Function.protoype的属性,也会出现在Function的prototype chain中。
四、About if they refer to objects joined to each other
Objects Joined其实是Spec建议实现者(如V8、SpiderMonkey)采用的底层优化手段。
function a(){
function b(){}
return b
}
var c = a()
var d = a()
// 假如JavaScript Engine实现了Objects Joined,那么
c === d 返回值为true。因为a中定义b函数啥都一样,所以底层实现可以不再生成一个新的Function object,从而从空间和时间上降低消耗。
五 、Conclusion
之前看了很多讲述instanceof的文章但始终对它理解得不透彻,看来还是看Spec比较实在。
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4231454.html 肥仔John^_^
六、Thanks
http://www.w3cfuns.com/article-5597466-1-1.html
http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/
JS魔法堂:再识instanceof的更多相关文章
- JS魔法堂:jsDeferred源码剖析
一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计& ...
- JS魔法堂:那些困扰你的DOM集合类型
一.前言 大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②. 抛namedItem is undefined的异常 var nodes = documen ...
- JS魔法堂:追忆那些原始的选择器
一.前言 ...
- JS魔法堂:不完全国际化&本地化手册 之 实战篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:属性、特性,傻傻分不清楚
一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...
- JS魔法堂:doctype我们应该了解的基础知识
一.前言 什么是doctype?其实我们一直使用,却很少停下来看清楚它到底是什么,对网页有什么作用.本篇将和大家一起探讨那个默默无闻的doctype吧! 二.什么是doctype doctype或DT ...
- JS魔法堂:精确判断IE的文档模式by特征嗅探
一.前言 苦逼的前端攻城狮都深受浏览器兼容之苦,再完成每一项功能前都要左顾右盼,生怕浏览器不支持某个API,生怕原生API内含臭虫因此判断浏览器类型和版本号成了不可绕过的一道关卡,而特征嗅探是继浏览器 ...
- JS魔法堂:不完全国际化&本地化手册 之 理論篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:判断节点位置关系
一.前言 在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅. 二 ...
- JS魔法堂:LINK元素深入详解
一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css&quo ...
随机推荐
- 年底了,特贡献一些C#有意思的算法题
2013年,即将要过去了.屌丝C#程序员们拿到了年终奖不?是不是又想蠢蠢欲动了?是不是想通过跳槽来为自己实现加薪的梦想?好吧,跳槽之前还是做点准备吧,准备好C#的笔试吧.这里我收集了些奉献给大家,大家 ...
- Linux及文件系统基本介绍
Linux及文件系统基本介绍 1 互联网行业现状 在服务器端市场: 超级计算机 2014年11月的数据显示前500系统中的485个系统都在运行着 Linux 的发布系统,而仅仅只有一台运行着 Wi ...
- zendframework 2
我想我的生活需要新的挑战 zf2整个框架里面都应用了namespace,并且他的每个模块,我们都可以根据自己的需要去命名路径,对我来说,zf2的模块化更加的清晰,对于外包来说,或许很方便. 创建他,我 ...
- Preserving Remote IP/Host while proxying
因为这个文章用一般手段看不到,所以摘录下来备用 (From http://kasunh.wordpress.com/2011/10/11/preserving-remote-iphost-while- ...
- 换个角度理解云计算之MapReduce(二)
接上篇 3.Combiner操作 前面讲完Map操作,总结一下就是:一个大文件,分成split1~5,对应于Map1~5,每一个Map处理一个split,每一个split的每一行,会用每一个Map的m ...
- C# 版 flvmerge:快速合并多个flv文件
网上的视频很多都是分片的flv文件,怎么把他们合为一体呢?GUI工具就不考虑了,不适合批量执行,不适合在后台运行.有没有命令行工具或库可以实现呢? ffmpeg 提供了一个方法: (1)先把flv文件 ...
- Homebrew简介及安装
Homebrew官网 http://brew.sh/index_zh-cn.html Homebrew是神马 linux系统有个让人蛋疼的通病,软件包依赖,好在当前主流的两大发行版本都自带了解决方案, ...
- MyBatis学习总结(二)——使用MyBatis对表执行CRUD操作
一.使用MyBatis对表执行CRUD操作--基于XML的实现 1.定义sql映射xml文件 userMapper.xml文件的内容如下: 1 <?xml version="1.0&q ...
- [Java面试九]脚本语言知识总结.
核心内容概述 1.JavaScript加强,涉及到ECMAScript语法.BOM对象.DOM对象以及事件. 2.Ajax传统编程. 3.jQuery框架,九种选择器为核心学习内容 4.JQuery ...
- 学习ASP.NET MVC(六)——我的第一个ASP.NET MVC 编辑页面
在上一文章中由Entity Framework(实体框架)去实现了对数据库的CURD操作.在本篇文章中,主要是调试修改自动生成的动作方法和视图,以及调试编辑功能与编辑功能的Book控制器. 首先,在V ...