✍️ Tangxt | ⏳ 2020-06-15 | 🏷️ JS 专题 |
typeof null
-> "object"
-> null不是对象,它是空对象指针,一般我们用完了一个对象变量,就会把该对象赋值为 null
,就目前的Chrome浏览器而言,它时不时就会去回收咩有被占用的堆内存,如本来一个 obj
变量,占用了 {name:'frank'}
这个堆内存,我们用完了这个堆内存,我们就 obj = null
,这样一来 {name:'frank'}
就咩有被 obj
占用了,没有被占用,那么就会被Chrome浏览器的辣鸡回收机制给回收掉! -> IE的辣鸡回收机制与chrome不同typeof
很强大的话,就不需考虑其它检测方式了面试的坑(也不算是坑):
console.log(typeof []); // => "object"
console.log(typeof typeof []); // => "string"
运算规则是:从右往左!
instanceof
/constructor
__proto__
或者 prototype
改动原型链的动向,所以基于 instanceof
检测出来的结果并不一定是准确的__proto__
,虽说也是所属类的实例,在JS中也可以调取所属类原型上的方法,但是instanceof是不认的测试:
console.log(12 instanceof Number); // => false
console.log(new Number(12) instanceof Number); // => true
console.log([] instanceof Array); // => true
console.log([] instanceof Object); // => true
关于浏览器的隐式转换:
(12).__proto__
<=>new Number(12).__proto__
可以看到 12
明明是Number的实例,但是 instanceof
就是不认
搞坏 instanceof
的测试:
let arr = []
function fn() {}
arr.__proto__ = fn.prototype
arr instanceof Array // -> false
可以看到,通过JS,原型链基于 __proto__
或者 prototype
是可以随意改动的,因此, instanceof
检测出来的结果并不一定准确! -> 所以 instanceof
只能用作于一个简单的参考姿势!
小结:
instanceof
检测基本类型,而 typeof
则可以 -> instanceof
检测引用类型值要比 typeof
准确点!如 /^$/ instanceof RegExp -> true
同 instanceof
基本一样
// 私有constructor属性找不到就找公有的
console.log([].constructor === Array) // -> true -> 表示 [] 为数组
console.log([].constructor === Object) // -> false -> 不可跨级
console.log(({}).constructor === Array) // -> false
console.log((12).constructor === Number) // -> true
constructor
这个值也是可以随便改的,而且比 instanceof
还要更恶心!如:
let arr = []
Array.prototype.constructor = null
arr.constructor === Array // -> false
凡是可以随便改的,都是不严谨的,或多或少会出现潜在的bug,不过一般来说,我们基本都不会去动 constructor
的指向,或者是原型链的指向……
总之,对于基本类型而言,我们也是可以用 constructor
来搞的! -> 简单参考用 constructor
和 instanceof
这两个东西吧!
就上边我们所看到的那样,不管是 typeof
、 instanceof
,还是 constructor
,都有自己很多的局限性
那么是否存在一种没有局限性的方案呢?
有的,那就是 Object.prototype.toString
方法啦! -> 注意,这是Object原型上的 toString
方法!可不是其它类(如Array等)上的 toString
方法!
Object.prototype.toString.call
没有局限性的方案!
注意,也是有 Null
和 Undefined
这两个内置类的,只是我们开发者无法使用罢了 -> 你一使用就会报错!
敲黑板:
Object原型上的
toString
方法可不是用来转换为字符串的,而是返回当前实例所属类的信息
写法(本质上是其它类型数据的方法借用,就像是伪数组,如arguments借用数组的slice方法变为真正的数组):
Object.prototype.toString.call([value])
// or
({}).toString.call([value])
返回值的格式:
"[object 所属类信息]"
可能有的结果:
"[object Object/Array/RegExp/Date/Function/Null/Undefined/Number/String/Boolean/Symbol...]"
原理:
数组字面量不需要这样
([])
,而普通对象是需要的 ->({})
,因为{}
在JS中有太多含义了,如代码块,对象字面量等等,所以如果你这样{}.toString()
,那么就会报语法错误!
测试(检测各种值的数据类型):
即便你更改原型或原型链:
也不会影响 ({}).toString.call()
的判断!
所以:
这种方式基本上没有什么局限性,是检测数据类型最准确的方式
但在我看来,这还是有弊端的:
typeof
-> 为啥用这个? -> 因为简单呀!没必要搞那些复杂的姿势{}
:
instanceof
就搞定,如 xxx instanceof Array
」,或者另一种姿势: xxx.constructor === Array
({}).toString.call(xxx)
这种姿势来搞!1
,那么它就是 Number
类型的 -> 我看到 /^$/
这种结构的数据,那么它就是 RegExp
类型的 -> 我看到 []
这种结构的数据,那么它就是 Array
类型的 -> 我看到 {}
这种结构的数据,那么它就是 Object
类型的 -> 我看到 function(){}
这种结构的数据,那么它就是 Function
类型的 -> ……