JavaScript实现一个instanceof

上一篇学习总结留下了一个疑问,基本类型的instanceof和我们预期的不一致

JavaScript原型链的学习总结_IronKee的博客-CSDN博客关于原型链的学习总是很迷,看了过段时间就忘,或者不理解,看多了就走火入魔了。这次换个思路来学习,先搞懂JavaScript中的Object和Function的关系、起源。https://blog.csdn.net/IronKee/article/details/120179373

1.instanceof

首先学习下instanceof的定义

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性

The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor. 

从字面意思理解,就是判断一个对象(实例)的原型链上是否存在一个构造函数的prototype,也就是顺着__proto__一直找prototype

对照字面意思,写一下instanceof的实现

// 判断constructor.prototype是否出现在obj的原型链上
function instanceof (obj, constructor) {
  // 获取实例对象obj的原型对象
  let proto = obj.__proto__
  while (true) {
    if (proto === null) {
      return false//原型链最终指向null,如果顺着proto一直找到null,还没找到原型,那就return false
    }
    if (proto === constructor.prototype) {
      return true//找到原型,return true
    }
    proto = proto.__proto__//顺着__proto__继续往上找
  }
}


function Foo(){}
let foo = new Foo()
instanceof(foo,Foo) //true
instanceof(foo,Function) //true
instanceof(foo,Object) //true

从上一篇学习总结可以知道,原型链最终指向null,所以如果顺着proto一直找到null,还没找到原型,那就return false

分析以下判断

 

Object.__proto__指向Function.prototype,Function.prototype.__proto__指向Object.prototype

所以前两个为true

Function.__proto__指向Function.prototype,Function.prototype.__proto__指向Object.prototype

所以3,4也为true

String和Number是函数,也都是先指向Function.prototype,再指向Object.prototype,所以5,6,7,8也都为true

Number instanceof Number和String instanceof String,为false,因为Number.__proto__和Number.prototype完全是两回事,String同理

同样的,新建一个函数Foo也是如此,左边Foo.__proto__是Function.prototype,Function.prototype.__proto__指向Object.prototype,最终只能找到这里,而右边是Foo.prototype,无法相等

回到一开始的问题,看一下定义,是针对的an object ,所以说String Number Boolean Undefined Null Symbol这些基本数据类型就不要用instanceof了,用的话会出现和你预想不一样的问题,就比如开头贴图的问题,关于基本类型的讨论,可以看看这个javascript - Why does instanceof return false for some literals? - Stack Overflow

let bool1 = true;//基本数据类型,并不是一个对象,也不是由Boolean实例化出来的,bool1不是Boolean的实例
let bool2 = new Boolean("true");//使用Boolean构造的实例化对象
console.log(bool1 instanceof Boolean) //false
console.log(bool2 instanceof Boolean) //true

Boolean("true") instanceof Boolean //false,没有使用new所以并不是使用构造函数实例化,而是使用Boolean这个函数返回了一个布尔值, 所以为false

 instanceof 语法 object instanceof constructor,规范明确地规定确认对象为object,所以左边如果不是对象的话,一定会返回false

还有null比较特殊,虽然null是Object类型,但是null instanceof Object也是false,因为null是空对象不具有任何对象的特性,上面也没有__proto__属性

既然提到这里,正好有个疑问,var str = 'str';str.toString();

为何str不是对象,但却可以调用String构造函数上的方法?

这就要看JS的规定了

简单理解就是,当原始数据类型(boolean,Number、String)在调用方法时,JS 将会创建对象,以便调用方法属性,而在使用完毕后将会销毁该对象。

原始值被当作构造函数创建的一个对象来使用时,JS 会将其转换为一个对象,以便其可以使用对象的特性(如方法),而后抛弃对象性质,并将它变回到原始值。

这就牵扯到基本包装类型了,基本类型中Boolean,Number,String又是基本包装类型,这三个基本类型都有自己对应的包装对象。包装对象,其实就是对象,有相应的属性和方法。调用方法的过程,是在后台发生的。

具体包装对象过程“模拟”可以参考这篇文章js 中的基本类型,引用类型,基本包装类型https://segmentfault.com/a/1190000013003663

为什么基本类型可以调用方法——以字符串为例_weixin_30578677的博客-CSDN博客引用类型中的基本包装类型对于str.substring(2)这种方法我们经常使用,var str = "hello world"; var s1 = str.substring(2);可是str明明是个字符串(基本类型),又不是对象,为什么可以调用方法呢。。。。解释说明:当我们用str这个字符串调用方法时,后台已经为我们做了一系列操作,让我们来看看究竟都做了哪些操作呢...https://blog.csdn.net/weixin_30578677/article/details/95375102

2.typeof

typeof运算符的返回类型为字符串,值包括如下几种:

'undefined'   --未定义的变量或值

'boolean'     --布尔类型的变量或值

'string'        --字符串类型的变量或值

'number'      --数字类型的变量或值

'object'       --对象类型的变量或值,或者null(这个是js历史遗留问题,将null作为object类型处理)

'function'     --函数类型的变量或值



需要特别注意的几点,NaN返回number,null返回Object,new String()的实例返回object,class返回的是function

typeof具有一定的局限性,比如数组返回的也是object,用来判断数组的话就不合适了,这时候可以选择使用instanceof

 

具体是用instanceof还是typeof,要看具体情况

 3.Object.prototype.toString()

console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
function Person(){};
console.log(Object.prototype.toString.call(new Person));//[object Object]

可以看到这个方法能判断大多类型,但是针对我们自己创建的function,比如Person,返回的也是[object object],其余场景基本都覆盖到了,可以截取前8位到最后一位,作为类型判断

上一篇留了一个思考题,Object.prototype.toString和Object.toString是一回事吗?
答案是:不是。 因为Object.toString是Object.__proto__.toString,也就是去上一级的原型中拿的方法,是Function.prototype.toString,和Object.prototype.toString根本不是同一个函数。同理Number,String,Array里的toString也和Object.prototype.toString不一样,而且它们也都被重写了toString方法

可以测试一下,如果删除Array上的toString,我们知道顺着原型链一直找会找到Object.prototype上,这时候会调用Object原型上的toString了,通过控制台打印可以看到输出的是[object Array]

var arr=[1,2,3];
console.log(Array.prototype.hasOwnProperty("toString"));//true
console.log(arr.toString());//1,2,3
delete Array.prototype.toString;//delete操作符可以删除实例属性
console.log(Array.prototype.hasOwnProperty("toString"));//false
console.log(arr.toString());//"[object Array]"
console.log(Object.prototype.toString.call(arr));//"[object Array]"

Object.prototype.toString.call(arr),是为了调用Object原型的toString方法,将this指向arr,上下文改成arr,至于为什么返回[object xxxx]这种格式的字符串,可以看下这篇,ECMA的规范JavaScript:Object.prototype.toString方法的原理 - 知春里 - 博客园在JavaScript中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过Object.prototype.toString方法.var arr = [];console.log(Objecthttps://www.cnblogs.com/timejs/p/4722824.html

----暂时先这样----

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值