深入剖析JavaScript instanceof 运算符


Posted in Javascript onJune 14, 2019

instanceof 运算符简介

在 JavaScript 中,判断一个变量的类型尝尝会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 "object"。ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。例如:

清单 1. instanceof 示例

var oStringObject = new String("hello world"); 
console.log(oStringObject instanceof String); // 输出 "true"

这段代码问的是“变量 oStringObject 是否为 String 对象的实例?”oStringObject 的确是 String 对象的实例,因此结果是"true"。尽管不像 typeof 方法那样灵活,但是在 typeof 方法返回 "object" 的情况下,instanceof 方法还是很有用的。

instanceof 运算符的常规用法

通常来讲,使用 instanceof 就是判断一个实例是否属于某种类型。例如:

清单 2. instanceof 常规用法

// 判断 foo 是否是 Foo 类的实例
function Foo(){} 
var foo = new Foo(); 
console.log(foo instanceof Foo)//true

另外,更重的一点是 instanceof 可以在继承关系中用来判断一个实例是否属于它的父类型。例如:

清单 3. instanceof 在继承中关系中的用法

// 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
function Aoo(){} 
function Foo(){} 
Foo.prototype = new Aoo();//JavaScript 原型继承
var foo = new Foo(); 
console.log(foo instanceof Foo)//true 
console.log(foo instanceof Aoo)//true

上面的代码中是判断了一层继承关系中的父类,在多层继承关系中,instanceof 运算符同样适用。

你真的了解 instanceof 操作符吗?

看了上面的代码示例,是不是觉得 instanceof 操作符很简单,下面来看点复杂的用法。

清单 4. instanceof 复杂用法

console.log(Object instanceof Object);//true 
console.log(Function instanceof Function);//true 
console.log(Number instanceof Number);//false 
console.log(String instanceof String);//false 
console.log(Function instanceof Object);//true 
console.log(Foo instanceof Function);//true 
console.log(Foo instanceof Foo);//false

看了上面的代码是不是又晕头转向了?为什么 Object 和 Function instanceof 自己等于 true,而其他类 instanceof 自己却又不等于 true 呢?如何解释?要想从根本上了解 instanceof 的奥秘,需要从两个方面着手:1,语言规范中是如何定义这个运算符的。2,JavaScript 原型继承机制。

详细剖析 ECMAScript-262 edition 3 中 instanceof 运算符的定义

语言规范对中 instanceof 运算符的定义如下:

清单 5. 规范中 instanceof 运算符定义

11.8.6 The instanceof operator 
The production RelationalExpression: 
RelationalExpression instanceof ShiftExpression is evaluated as follows: 
1. Evaluate RelationalExpression. 
2. Call GetValue(Result(1)).// 调用 GetValue 方法得到 Result(1) 的值,设为 Result(2) 
3. Evaluate ShiftExpression. 
4. Call GetValue(Result(3)).// 同理,这里设为 Result(4) 
5. If Result(4) is not an object, throw a TypeError exception.// 如果 Result(4) 不是 object,
//抛出异常
/* 如果 Result(4) 没有 [[HasInstance]] 方法,抛出异常。规范中的所有 [[...]] 方法或者属性都是内部的,
在 JavaScript 中不能直接使用。并且规范中说明,只有 Function 对象实现了 [[HasInstance]] 方法。
所以这里可以简单的理解为:如果 Result(4) 不是 Function 对象,抛出异常 */ 
6. If Result(4) does not have a [[HasInstance]] method, 
throw a TypeError exception. 
// 相当于这样调用:Result(4).[[HasInstance]](Result(2)) 
7. Call the [[HasInstance]] method of Result(4) with parameter Result(2). 
8. Return Result(7). 
// 相关的 HasInstance 方法定义
15.3.5.3 [[HasInstance]] (V) 
Assume F is a Function object.// 这里 F 就是上面的 Result(4),V 是 Result(2) 
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.// 如果 V 不是 object,直接返回 false 
2. Call the [[Get]] method of F with property name "prototype".// 用 [[Get]] 方法取 
// F 的 prototype 属性
3. Let O be Result(2).//O = F.[[Get]]("prototype") 
4. If O is not an object, throw a TypeError exception. 
5. Let V be the value of the [[Prototype]] property of V.//V = V.[[Prototype]] 
6. If V is null, return false. 
// 这里是关键,如果 O 和 V 引用的是同一个对象,则返回 true;否则,到 Step 8 返回 Step 5 继续循环
7. If O and V refer to the same object or if they refer to objects 
joined to each other (section 13.1.2), return true. 
8. Go to step 5.

上面的规范定义很晦涩,而且看起来比较复杂,涉及到很多概念,但把这段规范翻译成 JavaScript 代码却很简单,如下:

清单 6. JavaScript instanceof 运算符代码

function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
var O = R.prototype;// 取 R 的显示原型
L = L.__proto__;// 取 L 的隐式原型
while (true) { 
if (L === null) 
return false; 
if (O === L)// 这里重点:当 O 严格等于 L 时,返回 true 
return true; 
L = L.__proto__; 
} 
}

JavaScript 原型继承机制

由于本文主要集中在剖析 JavaScript instanceof 运算符,所以对于 JavaScript 的原型继承机制不再做详细的讲解,下面参考来自 http://www.mollypages.org/misc/js.mp 的一张图片,此图片详细的描述了 JavaScript 各种对象的显示和隐式原型链结构。

由其本文涉及显示原型和隐式原型,所以下面对这两个概念作一下简单说明。在 JavaScript 原型继承结构里面,规范中用 [[Prototype]] 表示对象隐式的原型,在 JavaScript 中用 __proto__ 表示,并且在 Firefox 和 Chrome 浏览器中是可以访问得到这个属性的,但是 IE 下不行。

所有 JavaScript 对象都有 __proto__ 属性,但只有 Object.prototype.__proto__ 为 null,前提是没有在 Firefox 或者 Chrome 下修改过这个属性。这个属性指向它的原型对象。 至于显示的原型,在 JavaScript 里用 prototype 属性表示,这个是 JavaScript 原型继承的基础知识,在这里就不在叙述了。

深入剖析JavaScript instanceof 运算符

JavaScript 原型链

讲解 instanceof 复杂用法

有了上面 instanceof 运算符的 JavaScript 代码和原型继承图,再来理解 instanceof 运算符将易如反掌。下面将详细讲解 Object instanceof Object,Function instanceof Function 和 Foo instanceof Foo 三个示例,其它示例读者可自行推演。

清单 7. Object instanceof Object

// 为了方便表述,首先区分左侧表达式和右侧表达式
ObjectL = Object, ObjectR = Object; 
// 下面根据规范逐步推演
O = ObjectR.prototype = Object.prototype 
L = ObjectL.__proto__ = Function.prototype 
// 第一次判断
O != L 
// 循环查找 L 是否还有 __proto__ 
L = Function.prototype.__proto__ = Object.prototype 
// 第二次判断
O == L 
// 返回 true

清单 8. Function instanceof Function

// 为了方便表述,首先区分左侧表达式和右侧表达式
FunctionL = Function, FunctionR = Function; 
// 下面根据规范逐步推演
O = FunctionR.prototype = Function.prototype 
L = FunctionL.__proto__ = Function.prototype 
// 第一次判断
O == L 
// 返回 true

清单 9. Foo instanceof Foo

// 为了方便表述,首先区分左侧表达式和右侧表达式
FooL = Foo, FooR = Foo; 
// 下面根据规范逐步推演
O = FooR.prototype = Foo.prototype 
L = FooL.__proto__ = Function.prototype 
// 第一次判断
O != L 
// 循环再次查找 L 是否还有 __proto__ 
L = Function.prototype.__proto__ = Object.prototype 
// 第二次判断
O != L 
// 再次循环查找 L 是否还有 __proto__ 
L = Object.prototype.__proto__ = null 
// 第三次判断
L == null 
// 返回 false

简析 instanceof 在 Dojo 继承机制中的应用

在 JavaScript 中,是没有多重继承这个概念的,就像 Java 一样。但在 Dojo 中使用 declare 声明类时,是允许继承自多个类的。下面以 Dojo 1.6.1 为例。

清单 10. Dojo 中多重继承

dojo.declare("Aoo",null,{}); 
dojo.declare("Boo",null,{}); 
dojo.declare("Foo",[Aoo,Boo],{}); 
var foo = new Foo(); 
console.log(foo instanceof Aoo);//true 
console.log(foo instanceof Boo);//false 
console.log(foo.isInstanceOf(Aoo));//true 
console.log(foo.isInstanceOf(Boo));//true

上面的示例中,Foo 同时继承自 Aoo 和 Boo,但当使用 instanceof 运算符来检查 foo 是否是 Boo 的实例时,返回的是 false。实际上,在 Dojo 的内部,Foo 仍然只继承自 Aoo,而通过 mixin 机制把 Boo 类中的方法和属性拷贝到 Foo 中,所以当用 instanceof 运算符来检查是否是 Boo 的实例时,会返回 false。所以 Dojo 为每个类的实例添加了一个新的方法叫 isInstanceOf,用这个方法来检查多重继承。

结束语

本文详细介绍了 JavaScript 语言中 instanceof 运算符,并且结合语言规范深入剖析了此操作符的算法。对读者使用 JavaScript 编写复杂的面向对象程序会有很大的帮助。本文所有代码在 Firefox 15 下通过测试。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
DOM相关内容速查手册
Feb 07 Javascript
JS 判断代码全收集
Apr 28 Javascript
谈谈关于JavaScript 中的 MVC 模式
Apr 11 Javascript
jquery选择器大全 全面详解jquery选择器
Mar 06 Javascript
JS实现定时自动关闭DIV层提示框的方法
May 11 Javascript
jQuery+Ajax请求本地数据加载商品列表页并跳转详情页的实现方法
Jul 12 jQuery
jQuery EasyUI window窗口使用实例代码
Dec 25 jQuery
vue 优化CDN加速的方法示例
Sep 19 Javascript
使用Vue实现移动端左滑删除效果附源码
May 16 Javascript
jQuery实现的鼠标拖动画矩形框示例【可兼容IE8】
May 17 jQuery
javascript中可能用得到的全部的排序算法
Mar 05 Javascript
javascript实现随机抽奖功能
Dec 30 Javascript
ES6 Promise对象的含义和基本用法分析
Jun 14 #Javascript
ES6顶层对象、global对象实例分析
Jun 14 #Javascript
ES6数组与对象的解构赋值详解
Jun 14 #Javascript
简单了解Ajax表单序列化的实现方法
Jun 14 #Javascript
通过JS深度判断两个对象字段相同
Jun 14 #Javascript
CKEditor 4.4.1 添加代码高亮显示插件功能教程【使用官方推荐Code Snippet插件】
Jun 14 #Javascript
利用JavaScript将Excel转换为JSON示例代码
Jun 14 #Javascript
You might like
PHP循环输出指定目录下的所有文件和文件夹路径例子(简单实用)
2014/05/10 PHP
php精确的统计在线人数的方法
2015/10/21 PHP
PHP Cookei记录用户历史浏览信息的代码
2016/02/03 PHP
修改Laravel5.3中的路由文件与路径
2016/08/10 PHP
PHP实现基于面向对象的mysqli扩展库增删改查操作工具类
2017/07/18 PHP
有一段有意思的代码-javascript现实多行信息
2007/08/26 Javascript
Ajax同步与异步传输的示例代码
2013/11/21 Javascript
一个CSS+jQuery实现的放大缩小动画效果
2014/02/19 Javascript
AngularJS入门教程之Hello World!
2014/12/06 Javascript
jQuery中:gt选择器用法实例
2014/12/29 Javascript
JavaScript 学习笔记之基础中的基础
2015/01/13 Javascript
javascript实现根据3原色制作颜色选择器的方法
2015/07/17 Javascript
关于javascript的一些知识以及循环详解
2016/09/12 Javascript
vue.js通过自定义指令实现数据拉取更新的实现方法
2016/10/18 Javascript
jQuery双向列表选择器DIV模拟版
2016/11/01 Javascript
微信小程序 UI与容器组件总结
2017/02/21 Javascript
JS字符串统计操作示例【遍历,截取,输出,计算】
2017/03/27 Javascript
解决Vue.js父组件$on无法监听子组件$emit触发事件的问题
2018/09/12 Javascript
NodeJs 文件系统操作模块fs使用方法详解
2018/11/26 NodeJs
详解vue后台系统登录态管理
2019/04/02 Javascript
vue弹出框组件封装实例代码
2019/10/31 Javascript
Element实现表格嵌套、多个表格共用一个表头的方法
2020/05/09 Javascript
Vue移动端项目实现使用手机预览调试操作
2020/07/18 Javascript
vue实现简单全选和反选功能
2020/09/15 Javascript
python基础教程之字典操作详解
2014/03/25 Python
python根据日期返回星期几的方法
2015/07/06 Python
django开发之settings.py中变量的全局引用详解
2017/03/29 Python
Python机器学习之决策树算法
2017/12/22 Python
python中字符串比较使用is、==和cmp()总结
2018/03/18 Python
python并发编程多进程 互斥锁原理解析
2019/08/20 Python
python解析xml文件方式(解析、更新、写入)
2020/03/05 Python
Python list和str互转的实现示例
2020/11/16 Python
python3判断IP地址的方法
2021/03/04 Python
html5 Canvas画图教程(4)—未闭合的路径及渐变色的填充方法
2013/01/09 HTML / CSS
香港永安旅游网:Wing On Travel
2017/04/10 全球购物
2015年置业顾问工作总结
2015/04/07 职场文书