第 21 题:有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣Object.prototype.toString.call() 、 instanceof 以及 Array.isArray()
xueqingxiao opened this issue · 34 comments
1. Object.prototype.toString.call()
每一个继承 Object 的对象都有 toString
方法,如果 toString
方法没有重写的话,会返回 [Object type]
,其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用 toString
方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文。
const an = ['Hello','An'];
an.toString(); // "Hello,An"
Object.prototype.toString.call(an); // "[object Array]"
这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。
Object.prototype.toString.call('An') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call({name: 'An'}) // "[object Object]"
Object.prototype.toString.call()
常用于判断浏览器内置对象时。
更多实现可见 谈谈 Object.prototype.toString
2. instanceof
instanceof
的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype
。
使用 instanceof
判断一个对象是否为数组,instanceof
会判断这个对象的原型链上是否会找到对应的 Array
的原型,找到返回 true
,否则返回 false
。
[] instanceof Array; // true
但 instanceof
只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。
[] instanceof Object; // true
3. Array.isArray()
-
功能:用来判断对象是否为数组
-
instanceof 与 isArray
当检测Array实例时,
Array.isArray
优于instanceof
,因为Array.isArray
可以检测出iframes
var iframe = document.createElement('iframe'); document.body.appendChild(iframe); xArray = window.frames[window.frames.length-1].Array; var arr = new xArray(1,2,3); // [1,2,3] // Correctly checking for Array Array.isArray(arr); // true Object.prototype.toString.call(arr); // true // Considered harmful, because doesn't work though iframes arr instanceof Array; // false
-
Array.isArray()
与Object.prototype.toString.call()
Array.isArray()
是ES5新增的方法,当不存在Array.isArray()
,可以用Object.prototype.toString.call()
实现。if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; }
从Stack Overflow扒来的资料:
结论:toString.call和isArray的性能一样差,instanceof稍好,constructor性能最好
优劣我只能看到这么多了。非要咬文嚼字大概就是Array.isArray在老浏览器需要polyfill,然后又跟toString.call一样了。
初次之外看到一个神奇之处,instanceof不能检测来自iframe的数组。就是官方文档上,window.frames[xx].Array构造出来的数组。
对于性能问题,我测试几遍的结果 Array.isArray 的性能最好,instanceof 比 toString.call 稍微好了一点点
测试链接: https://jsperf.com/judging-array-type/
对于其中的性能差异的原理,大家可以讨论一下的
arr.constructor === Array
constructor可以被修改?
constructor可以被修改?
目测是可以被修改的
arr.constructor = '123'
arr. proto .constructor = '123'
可以console.log(arr.constructor)看下结果,然后再看一下__proto__中的constructor的结果
但当除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文。
这里感觉不太清晰。事实上应该不是改变 toString 的上下文,而是改变调用的 toString 方法:
const arr = [1, 2]
arr.toString === Object.prototype.toString // false, 所以两者不同,实际上数组上重写了 toString 方法
const o = {o: 1}
o.toString === Object.prototype.toString // true, 所以对象默认不需要如此调用。但如果将对象的方法改写就不一定了
o.toString = function changedToString() {
return 'haha';
}
o.toString() // 'haha'
Object.prototype.toString.call(o) // '[object Object]'. 发现 Object.prototype.toString 也是可以被改写的...
call
方法的第一个参数会被当作 this
,所以 arr.toString()
与 Object.prototype.toString.call(arr)
并没有改变 this
,而是改变了调用的函数。
typeof 是什么原理呢?
哪位大神给讲讲
typeof 是什么原理呢?
哪位大神给讲讲
typeof 只能检测 基本数据类型,包括boolean、undefined、string、number、symbol,而null ,Array、Object ,使用typeof出来都是Objec。无法检测具体是哪种引用类型。
想仿写一下Object.prototype.toString,但是卡在了null 和 undefined的判断上,返回[object Window]... ...
instanceof是判断类型的prototype是否出现在对象的原型链中,但是对象的原型可以随意修改,所以这种判断并不准确。
const obj = {}
obj.__proto__ = Array.prototype
// Object.setPrototypeOf(obj, Array.prototype)
obj instanceof Array // true
typeof 是什么原理呢?
哪位大神给讲讲typeof 只能检测 基本数据类型,包括boolean、undefined、string、number、symbol,而null ,Array、function、Object ,使用typeof出来都是Objec。无法检测具体是哪种引用类型。
函数的typeof是'function'
Object.prototype.toString.call()
这个方法也不是完全靠谱的啊,
如果遇到对象的属性 Symbol.toStringTag
被改成了"Array"
怎么办啊
应该要加上instanceof
和typeof
三重判断才行
typeof不能校验object的其他类型,引用类型除了function都不能区分
instanceof不能校验原始值类型
'a' instanceof String
// false
Object.prototype.toString.call()
不能校验自定义类型
function Animal (){}
let a = new Animal()
Object.prototype.toString.call(a)
"[object Object]"
具体来讲:每一个继承 Object 的对象都有 toString 方法,该方法返回[Object type];Array、function、Date等都继承于Object,但它们都改写了toString方法,所以阻断了对Object原型上toSting方法的调用。
验证:可以使用delete操作符删除原型实例上的toString方法,然后再调用toString,就可以调用到Object原型上的toString了。
题中用法是:直接调用原型方法,使用call改变this指向,以此来使对象直接调用原型方法。
但当除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文。
这里感觉不太清晰。事实上应该不是改变 toString 的上下文,而是改变调用的 toString 方法:const arr = [1, 2] arr.toString === Object.prototype.toString // false, 所以两者不同,实际上数组上重写了 toString 方法 const o = {o: 1} o.toString === Object.prototype.toString // true, 所以对象默认不需要如此调用。但如果将对象的方法改写就不一定了 o.toString = function changedToString() { return 'haha'; } o.toString() // 'haha' Object.prototype.toString.call(o) // '[object Object]'. 发现 Object.prototype.toString 也是可以被改写的...
call
方法的第一个参数会被当作this
,所以arr.toString()
与Object.prototype.toString.call(arr)
并没有改变this
,而是改变了调用的函数。
我觉得两者说的都没有问题,只是角度不同,对于Object的toString方法来说确实是修改了上下文,而对于Array来说,修改的是调用的toString方法
如果遇到精心设计的徦数组怎么判断呢?
var fakearray={length:0,__proto__:Array.prototype,[Symbol.toStringTag]:'Array'}
var fakearray=Object("fakestring")
fakearray.__proto__=Array.prototype
fakearray[Symbol.toStringTag]='Array'
用Array.isArray
判断结果是false
如果真数组的Symbol.toStringTag
属性被修改了,或者__proto__
被修改了,怎么判断?
var realarray=[]
realarray[Symbol.toStringTag]='Function'
realarray.__proto__=Function.prototype
用Array.isArray
判断结果是true
其实还可以用
JSON.stringify
来判断是不是数组
数组转json
是以[
开头,以]
结尾的
个人感觉题目有必要加上 typeof
想仿写一下Object.prototype.toString,但是卡在了null 和 undefined的判断上,返回[object Window]... ...
这两个都满足“不可变”性,也就是全局唯一,直接在你的判断函数中用 === 来判断好了,不等于这俩的再去做其他判断。
但当除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文。
这里感觉不太清晰。事实上应该不是改变 toString 的上下文,而是改变调用的 toString 方法:const arr = [1, 2] arr.toString === Object.prototype.toString // false, 所以两者不同,实际上数组上重写了 toString 方法 const o = {o: 1} o.toString === Object.prototype.toString // true, 所以对象默认不需要如此调用。但如果将对象的方法改写就不一定了 o.toString = function changedToString() { return 'haha'; } o.toString() // 'haha' Object.prototype.toString.call(o) // '[object Object]'. 发现 Object.prototype.toString 也是可以被改写的...
call
方法的第一个参数会被当作this
,所以arr.toString()
与Object.prototype.toString.call(arr)
并没有改变this
,而是改变了调用的函数。我觉得两者说的都没有问题,只是角度不同,对于Object的toString方法来说确实是修改了上下文,而对于Array来说,修改的是调用的toString方法
突然很好奇Object下原始toString都干了啥
isArray()不是es6新增吗
var arr = [1, 2, 3]
Array.isArray(arr) // es5新增的 如果不存在可以使用Object.prototype.toString.call(arr)
// Array.isArray 要优于instanceof 因为Array.isArray可以检测出iframe
arr instanceof Array
arr.constructor === Array // 性能最好
Object.prototype.toString.call(arr) === '[objecr Array]'
typeof 是什么原理呢?
哪位大神给讲讲
- typeof在判断一些引用类型时会分辨不出具体类型:
-
例如对象和数组;
typeof [1,2,3]; //object
typeof {}; //object -
还有typeof判断String/Array/Boolean/Number等等,都会返回'function',当作是String/Array/Boolean/Number类型的构造函数;
typeof Array; //function Array类型的构造函数
2.实际上js在底层存储变量的时候,会在变量的机器码的低位(1-3位)存储其类型信息,对象用的是000,null的所有机器码(不仅是1-3位)都是0,所以typeof还无法区分object 和 null;
Object.prototype.toString.call可以识别出所有的数据类型,比如
Object.prototype.toString.call({ }) // "[object Object]"
Object.prototype.toString.call([2,3]) // "[object Array]"
Object.prototype.toString.call('An') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
instanceof 不能使用在原始类型上,只能用在对象类型上,
left instance right,
如果left的原型链上能找到right的prototype,即left是right的一个实例,如果能找到返回为true,比如
[] instanceof Array //true
,
但是这样写区分不了对象和数组,因为
[] instanceof Object//也返回true
,
原因是所有对象类型的祖宗都是Object
isArray可以用来判断是否是数组,而且可以区分对象
上图解析:
- 原型链就是多个对象通过__proto__连接了起来
- 凡是对象,最终都可以通过__proto__寻址到Object.prototype
- 凡是函数,最终都可以通过__proto__寻址到Function.prototype
- 函数的prototype是一个对象
Object.prototype.toString.call()
万金油,啥类型都能准确的判断,并且没有兼容性问题
instanceof
虽然 [] instanceof Array 返回true,但是 [] instanceof Object 也是true,说不出有啥缺点
Array.isArray()
ES6才有的新方法,有兼容性问题
各位大佬轻喷😸
Object.prototype.toString.call()
万金油,啥类型都能准确的判断,并且没有兼容性问题
instanceof
虽然 [] instanceof Array 返回true,但是 [] instanceof Object 也是true,说不出有啥缺点Array.isArray()
ES6才有的新方法,有兼容性问题
各位大佬轻喷😸
杠一下,Array.isArray() 在 ES5 就有了,兼容性很好
1. Object.prototype.toString.call()
每一个继承 Object 的对象都有
toString
方法,如果toString
方法没有重写的话,会返回[Object type]
,其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用toString
方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文。const an = ['Hello','An']; an.toString(); // "Hello,An" Object.prototype.toString.call(an); // "[object Array]"这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。
Object.prototype.toString.call('An') // "[object String]" Object.prototype.toString.call(1) // "[object Number]" Object.prototype.toString.call(Symbol(1)) // "[object Symbol]" Object.prototype.toString.call(null) // "[object Null]" Object.prototype.toString.call(undefined) // "[object Undefined]" Object.prototype.toString.call(function(){}) // "[object Function]" Object.prototype.toString.call({name: 'An'}) // "[object Object]"
Object.prototype.toString.call()
常用于判断浏览器内置对象时。更多实现可见 谈谈 Object.prototype.toString
2. instanceof
instanceof
的内部机制是通过判断对象的原型链中是不是能找到类型的prototype
。使用
instanceof
判断一个对象是否为数组,instanceof
会判断这个对象的原型链上是否会找到对应的Array
的原型,找到返回true
,否则返回false
。[] instanceof Array; // true但
instanceof
只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。[] instanceof Object; // true3. Array.isArray()
- 功能:用来判断对象是否为数组
- instanceof 与 isArray
当检测Array实例时,Array.isArray
优于instanceof
,因为Array.isArray
可以检测出iframes
var iframe = document.createElement('iframe'); document.body.appendChild(iframe); xArray = window.frames[window.frames.length-1].Array; var arr = new xArray(1,2,3); // [1,2,3] // Correctly checking for Array Array.isArray(arr); // true Object.prototype.toString.call(arr); // true // Considered harmful, because doesn't work though iframes arr instanceof Array; // falseArray.isArray()
与Object.prototype.toString.call()
Array.isArray()
是ES5新增的方法,当不存在Array.isArray()
,可以用Object.prototype.toString.call()
实现。if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; }
null和undefined不能直接使用toString:Cannot read properties of null/undefined
1. Object.prototype.toString.call()
每一个继承 Object 的对象都有
toString
方法,如果toString
方法没有重写的话,会返回[Object type]
,其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用toString
方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文。const an = ['Hello','An']; an.toString(); // "Hello,An" Object.prototype.toString.call(an); // "[object Array]"这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。
Object.prototype.toString.call('An') // "[object String]" Object.prototype.toString.call(1) // "[object Number]" Object.prototype.toString.call(Symbol(1)) // "[object Symbol]" Object.prototype.toString.call(null) // "[object Null]" Object.prototype.toString.call(undefined) // "[object Undefined]" Object.prototype.toString.call(function(){}) // "[object Function]" Object.prototype.toString.call({name: 'An'}) // "[object Object]"
Object.prototype.toString.call()
常用于判断浏览器内置对象时。
更多实现可见 谈谈 Object.prototype.toString2. instanceof
instanceof
的内部机制是通过判断对象的原型链中是不是能找到类型的prototype
。
使用instanceof
判断一个对象是否为数组,instanceof
会判断这个对象的原型链上是否会找到对应的Array
的原型,找到返回true
,否则返回false
。[] instanceof Array; // true但
instanceof
只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。[] instanceof Object; // true3. Array.isArray()
- 功能:用来判断对象是否为数组
- instanceof 与 isArray
当检测Array实例时,Array.isArray
优于instanceof
,因为Array.isArray
可以检测出iframes
var iframe = document.createElement('iframe'); document.body.appendChild(iframe); xArray = window.frames[window.frames.length-1].Array; var arr = new xArray(1,2,3); // [1,2,3] // Correctly checking for Array Array.isArray(arr); // true Object.prototype.toString.call(arr); // true // Considered harmful, because doesn't work though iframes arr instanceof Array; // falseArray.isArray()
与Object.prototype.toString.call()
Array.isArray()
是ES5新增的方法,当不存在Array.isArray()
,可以用Object.prototype.toString.call()
实现。if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; }null和undefined不能直接使用toString:Cannot read properties of null/undefined
那是你没有定义变量....
Array.isArray()
是ES5新增的方法
Array.isArray()应该是ES6新增的方法哦
这里在理解的时候可能会把Object.prototype.toString.call()与[12].toString()方法相混淆,如果把这两个toString方法当作是一个,那么后续的理解会造成困难。因为当我们期待Object.prototype.toString.call([12])返回一个12时,发现控制台打印的却是【object array】。
首先,得理解这两个方法为什么不是一个?[12]是Array的实例,那么他的toString方法就得从Array这个构造者的prototype中去寻找,发现确实有这个方法,那么直接调用即可,不会再向上去寻找toString。而Object.prototype.toString方法属于最顶层的方法,从位置上来看他俩压根就不是一个。
其次,他俩有什么样的关系呢?通过下面这个式子就可以理解了。
[12].proto===Array.prototype
Array.prototype.proto===Object.prototype