JavaScript instanceof深度剖析以及Object.prototype.toString.call()使用
本文由segementfalt上的一道instanceof题引出:
var str = new String("hello world");
console.log(str instanceof String);//true
console.log(String instanceof Function);//true
console.log(str instanceof Function);//false
先抛开这道题,我们都知道在JS中typeof可以检测变量的基本数据类型。
let s = "abcd";
let b = true;
let i = 22;
let u;
let n = null;
let o = new Object();
alert(typeof s); //string
alert(typeof b); //boolean
alert(typeof i); //number
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object
从上面的信息可以看到,如果我们想知道某个值是什么类型的对象时,typeof就无能为力了!ECMAScript引入了instanceof来解决这个问题。instanceof用来判断某个构造函数的prototype是否在要检测对象的原型链上。
function Fn(){};
var fn = new Fn();
console.log(fn instanceof Fn) //true
//判断fn是否为Fn的实例,并且是否为其父元素的实例
function Aoo();
function Foo();
Foo.prototype = new Aoo();
let foo = new Foo();
console.log(foo instanceof Foo); //true
console.log(foo instanceof Aoo); //true
//instanceof 的复杂用法
console.log(Object instanceof Object) //true
console.log(Function instanceof Function) //true
console.log(Number instanceof Number) //false
console.log(Function instaceof Function) //true
console.log(Foo instanceof Foo) //false
看到上面的代码,你大概会有很多疑问吧。有人将ECMAScript-262 edition 3中对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__;
}
}
我们知道每个对象都有proto([[prototype]])属性,在js代码中用__proto__来表示,它是对象的隐式属性,在实例化的时候,会指向prototype所指的对象;对象是没有prototype属性的,prototype则是属于构造函数的属性。通过proto属性的串联构建了一个对象的原型访问链,起点为一个具体的对象,终点在Object.prototype。
Object instanceof Object :
// 区分左侧表达式和右侧表达式
ObjectL = Object, ObjectR = Object;
O = ObjectR.prototype = Object.prototype;
L = ObjectL.__proto__ = Function.prototype ( Object作为一个构造函数,是一个函数对象,所以他的__proto__指向Function.prototype)
// 第一次判断
O != L
// 循环查找 L 是否还有 __proto__
L = Function.prototype.__proto__ = Object.prototype ( Function.prototype是一个对象,同样是一个方法,方法是函数,所以它必须有自己的构造函数也就是Object)
// 第二次判断
O == L
// 返回 true
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
现在我们回到开始的题目。
对于第一条判断:
console.log(str.__proto__ === String.prototype); //true
console.log(str instanceof String);//true
第二条:
console.log(String.__proto__ === Function.prototype) //true
console.log(String instanceof Function);//true
第三条:
console.log(str__proto__ === String.prototype)//true
console.log(str__proto__.__proto__. === Function.prototype) //true
console.log(str__proto__.__proto__.__proto__ === Object.prototype) //true
console.log(str__proto__.__proto__.__proto__.__proto__ === null) //true
console.log(str instanceof Function);//false
总结以上:
str的原型链:
str ---> String.prototype ---> Object.prototype
String原型链:
String ---> Function.prototype ---> Object.prototype
由上面的讨论我们可以得知对于基本类型的判断我们可以使用typeof,而对于引用类型判断要用到instanceof,instanceof无法对原始类型进行判断。当然用constructor也可以来判断数据类型,但是要注意的是其值是可以被人为修改的,我们用另外一道面试题来谈论constuctor:
var A = function() {};
A.prototype = {};
var B = {};
console.log(A.constructor);//Function
console.log(B.constructor);//Object
var a = new A();
A.prototype = {};
var b = new A();
b.constructor = A.constructor;
console.log(a.constructor == A);//false
console.log(a.constructor == b.constructor);//false
console.log(a instanceof A);//false
console.log(b instanceof A);//true
在JS高程设计里面,作者是这样写的:无论什么时候,只要创建一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数原型对象。在默认情况下,所有的原型都会自动获得一个constructor属性,这个属性指向prototype所在函数的指针。
我们先来看下面这个例子:
function Foo() {
// ...
}
Foo.prototype.constructor === Foo; // true
var a = new Foo();
a.constructor === Foo; // true
a本身并没有constructor属性,虽然a.constructor确实指向Foo,但是这个属性并不是a由Foo“构造”的。实际上,.constructor 引用同样被委托给了Foo.prototype,而Foo.prototype.constructor 默认指向Foo。思考以下代码便知:
function Foo() { /* .. */ }
Foo.prototype = { /* .. */ }; // 创建一个新原型对象
var a1 = new Foo();
a1.constructor === Foo; // false!
a1.constructor === Object; // true!
我们回到抛出的第二道面试题:
var A = function() {};
A.prototype = {}; //此时对构造函数对象A的prototype属性重新复制,constructor属性不见了
var B = {};
console.log(A.constructor);//Function 函数的构造函数为 function Function()
console.log(B.constructor);//Object 普通object的构造函数为 function Object()
var a = new A(); new一个新对象
A.prototype = {};
var b = new A();
b.constructor = A.constructor;
console.log(a.constructor == A);//false a.constructor 为 Object
console.log(a.constructor == b.constructor);//false b.constructor 为 Function
console.log(a instanceof A);//false
console.log(b instanceof A);//true
上面的三种方式都有弊端,接下来引出我们的Object.prototype.toString.call()
Object.prototype.toString()可以通用的来判断原始数据类型和引用数据类型。
console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]
参考:
1: JS高程设计 第六章
2: 你不知道的JavaScript(上卷) 第五章
3: https://www.ibm.com/developerworks/cn/web/1306_jiangjj_jsinstanceof/
4: https://segmentfault.com/q/1010000003872816?_ea=403162
5: http://mobile.51cto.com/web-487689.htm
JavaScript instanceof深度剖析以及Object.prototype.toString.call()使用的更多相关文章
- JS四种判断数据类型的方法:typeof、instanceof、constructor、Object.prototype.toString.call()
1.typeof 1 console.log(typeof ""); //string 2 console.log(typeof 1); //number 3 console.lo ...
- instanceof, typeof, & Object.prototype.toString
/** * * @authors Your Name (you@example.org) * @date 2016-11-18 09:31:23 * @version $Id$ */instanceo ...
- JavaScript中toStirng()与Object.prototype.toString.call()方法浅谈
toStirng()与Object.prototype.toString.call()方法浅谈 一.toString()是一个怎样的方法?它是能将某一个值转化为字符串的方法.然而它是如何将一个值从一种 ...
- JavaScript:Object.prototype.toString方法的原理
在JavaScript中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过Object.prototype.toString方法. var arr = []; console.log(Obje ...
- 【JavaScript】Object.prototype.toString.call()进行类型判断
权声明:本文为博主原创文章,未经博主允许不得转载. op = Object.prototype, ostring = op.toString, ... function isFunction(it) ...
- JavaScript类型判断详解(Object.prototype.toString.call()方法进行数据类型的可靠判断)
前言 在编写一些类库中,我们经常需要判断一些未知的用户的输入和配置,故而需要进行一系列的类型判断.故而总结下JS是如何进行类型判断的 typeof typeof操作符返回一个字符串,表示未经计算的操作 ...
- Object.prototype.toString.call() 、 instanceof 以及 Array.isArray()判断数组的方法的优缺点
1. Object.prototype.toString.call() 每一个继承 Object 的对象都有 toString 方法,如果 toString 方法没有重写的话,会返回 [Object ...
- JavaScript:Object.prototype.toString进行数据类型判定
在JavaScript中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过Object.prototype.toString方法. var arr = []; console.log(Obje ...
- JavaScript中Object.prototype.toString方法的原理
在JavaScript中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过Object.prototype.toString方法. ? 1 2 var arr = []; console.lo ...
随机推荐
- (十五)golang--init函数
每一个源文件都会包含一个init函数,这个函数会在执行main()执行之前被调用,相当于python中的__init__ 注意细节:(1)如果一个文件同时包含全局变量定义,init函数和main函数, ...
- SWIG 3 中文手册——4. 脚本语言
目录 4 脚本语言 4.1 两种语言的概览 4.2 脚本语言如何调用 C? 4.2.1 包装器函数 4.2.2 变量链接 4.2.3 常量 4.2.4 结构体与类 4.2.5 代理类 4.3 构建脚本 ...
- 召唤神龙Ladon强化Cobalt Strike
Ladon5.5 20191109 wiki update 20191114 前言 Ladon 5.5支持Cobalt Strike,内置39个功能模块 加载脚本K8Ladon.cna,通过Ladon ...
- 微信小程序子组件样式不起作用的解决办法
今天我在编写微信小程序项目时,发现父组件引用子组件过后,子组件的样式不起作用,在上网查了很多解决办法后,成功解决了这一问题. 解决办法: 1.在全局样式文件app.wxss中引入子组件的样式,如 @i ...
- git 版本库之间的依赖 --submodule
自己做实验: 在github上建一个版本库B作为submoule: https://github.com/FrankKuiFang/testGit_submodule, 尝试将该版本库作为另一个库A ...
- 美国 | USA B1/B2 十年签证到手记
旅行生活美国 周一早上人还没到公司,就收到EMS快递员的电话,说有两份我的美国领事馆的签证快递,在前台要我签收一下. 美领馆的出签效率果然是高,上周三(8月10日)早上面签的,这三个工作日就把护照送到 ...
- LinkedHashMap 的核心就 2 点,搞清楚,也就掌握了
HashMap 有一个不足之处就是在迭代元素时与插入顺序不一致.而大多数人都喜欢按顺序做某些事情,所以,LinkedHashMap 就是针对这一点对 HashMap 进行扩展,主要新增了「两种迭代方式 ...
- EF启程--概念理解(数据库连接)
简介:Entity Framework 是一种支持 .NET 开发人员使用 .NET 对象处理数据库的对象关系映射程序 (O/RM). 它不要求提供开发人员通常需要编写的大部分数据访问代码. 其中有E ...
- .net core - 配置管理 - json文件配置
Json 文件配置 public class Startup { public Startup(IHostingEnvironment env) { var builder = new Configu ...
- 一种优化操作list、数组的多线程解决方案。
这几天接触到了一些操作list的功能,由于list太长,加上每条数据的处理时间,导致性能下降,正好利用学来的多线程知识和网上的资料结合实践一番,写出了一个通用类如下. /** * 操作数组的线程 * ...