上一篇学习总结留下了一个疑问,基本类型的instanceof和我们预期的不一致
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
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
----暂时先这样----