Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 21 题:有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣Object.prototype.toString.call() 、 instanceof 以及 Array.isArray() #23

Closed
xueqingxiao opened this issue Feb 26, 2019 · 34 comments
Labels

Comments

@xueqingxiao
Copy link

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray

@sisterAn
Copy link
Collaborator

sisterAn commented Feb 26, 2019

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]';
      };
    }

@bazinga-web
Copy link

https://www.cnblogs.com/onepixel/p/5126046.html

@lordGuan
Copy link

lordGuan commented Feb 26, 2019

从Stack Overflow扒来的资料:
结论:toString.call和isArray的性能一样差,instanceof稍好,constructor性能最好
优劣我只能看到这么多了。非要咬文嚼字大概就是Array.isArray在老浏览器需要polyfill,然后又跟toString.call一样了。
初次之外看到一个神奇之处,instanceof不能检测来自iframe的数组。就是官方文档上,window.frames[xx].Array构造出来的数组。

@jjeejj
Copy link
Contributor

jjeejj commented Feb 26, 2019

对于性能问题,我测试几遍的结果 Array.isArray 的性能最好,instanceof 比 toString.call 稍微好了一点点
image

测试链接: https://jsperf.com/judging-array-type/

对于其中的性能差异的原理,大家可以讨论一下的

@xiongchen2012
Copy link

arr.constructor === Array

@GitHdu
Copy link

GitHdu commented Feb 27, 2019

constructor可以被修改?

@thinkfish
Copy link

thinkfish commented Feb 28, 2019

constructor可以被修改?
目测是可以被修改的
arr.constructor = '123'
arr. proto .constructor = '123'
可以console.log(arr.constructor)看下结果,然后再看一下__proto__中的constructor的结果

@ginobilee
Copy link

@sisterAn

但当除了 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,而是改变了调用的函数。

@zhuzhh
Copy link

zhuzhh commented Apr 17, 2019

typeof 是什么原理呢?
哪位大神给讲讲

@NANAYWY
Copy link

NANAYWY commented Apr 18, 2019

typeof 是什么原理呢?
哪位大神给讲讲

typeof 只能检测 基本数据类型,包括boolean、undefined、string、number、symbol,而null ,Array、Object ,使用typeof出来都是Objec。无法检测具体是哪种引用类型。

@yygmind yygmind changed the title 第21题:有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣Object.prototype.toString.call() 、 instanceof 以及 Array.isArray() 第 21 题:有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣Object.prototype.toString.call() 、 instanceof 以及 Array.isArray() Apr 26, 2019
@xingorg1
Copy link

想仿写一下Object.prototype.toString,但是卡在了null 和 undefined的判断上,返回[object Window]... ...

@yujihu
Copy link

yujihu commented Jul 13, 2019

instanceof是判断类型的prototype是否出现在对象的原型链中,但是对象的原型可以随意修改,所以这种判断并不准确。

const obj = {}
obj.__proto__ = Array.prototype
// Object.setPrototypeOf(obj, Array.prototype)
obj instanceof Array // true

@yujihu
Copy link

yujihu commented Jul 13, 2019

typeof 是什么原理呢?
哪位大神给讲讲

typeof 只能检测 基本数据类型,包括boolean、undefined、string、number、symbol,而null ,Array、function、Object ,使用typeof出来都是Objec。无法检测具体是哪种引用类型。

函数的typeof是'function'

@masx200
Copy link

masx200 commented Jul 14, 2019

Object.prototype.toString.call()
这个方法也不是完全靠谱的啊,
如果遇到对象的属性 Symbol.toStringTag 被改成了"Array" 怎么办啊

应该要加上instanceoftypeof三重判断才行

@xufeiayang
Copy link

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]"

@aoyeshirenbianchou
Copy link

具体来讲:每一个继承 Object 的对象都有 toString 方法,该方法返回[Object type];Array、function、Date等都继承于Object,但它们都改写了toString方法,所以阻断了对Object原型上toSting方法的调用。
验证:可以使用delete操作符删除原型实例上的toString方法,然后再调用toString,就可以调用到Object原型上的toString了。
题中用法是:直接调用原型方法,使用call改变this指向,以此来使对象直接调用原型方法。

@xiaobai2017666
Copy link

@sisterAn

但当除了 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方法

@masx200
Copy link

masx200 commented Oct 14, 2019

如果遇到精心设计的徦数组怎么判断呢?

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

@masx200
Copy link

masx200 commented Oct 14, 2019

如果真数组的Symbol.toStringTag属性被修改了,或者__proto__被修改了,怎么判断?

var realarray=[]
realarray[Symbol.toStringTag]='Function'
realarray.__proto__=Function.prototype

Array.isArray判断结果是true

@masx200
Copy link

masx200 commented Oct 14, 2019

其实还可以用
JSON.stringify
来判断是不是数组
数组转json是以[开头,以]结尾的

@dmljc
Copy link

dmljc commented Oct 23, 2019

个人感觉题目有必要加上 typeof

@YMaster
Copy link

YMaster commented Dec 12, 2019

想仿写一下Object.prototype.toString,但是卡在了null 和 undefined的判断上,返回[object Window]... ...

这两个都满足“不可变”性,也就是全局唯一,直接在你的判断函数中用 === 来判断好了,不等于这俩的再去做其他判断。

@Hideer
Copy link

Hideer commented Feb 29, 2020

@sisterAn

但当除了 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都干了啥

@LemonSprit
Copy link

isArray()不是es6新增吗

@monxiaolee
Copy link

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]'

@sushiGump
Copy link

typeof 是什么原理呢?
哪位大神给讲讲

  1. 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;

@soraly
Copy link

soraly commented Jun 15, 2020

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可以用来判断是否是数组,而且可以区分对象
image
上图解析:

  1. 原型链就是多个对象通过__proto__连接了起来
  2. 凡是对象,最终都可以通过__proto__寻址到Object.prototype
  3. 凡是函数,最终都可以通过__proto__寻址到Function.prototype
  4. 函数的prototype是一个对象

@xsfxtsxxr
Copy link

Object.prototype.toString.call()

万金油,啥类型都能准确的判断,并且没有兼容性问题

instanceof

虽然 [] instanceof Array 返回true,但是 [] instanceof Object 也是true,说不出有啥缺点

Array.isArray()

ES6才有的新方法,有兼容性问题

各位大佬轻喷😸

@passing-j
Copy link

Object.prototype.toString.call()

万金油,啥类型都能准确的判断,并且没有兼容性问题

instanceof

虽然 [] instanceof Array 返回true,但是 [] instanceof Object 也是true,说不出有啥缺点

Array.isArray()

ES6才有的新方法,有兼容性问题

各位大佬轻喷😸

杠一下,Array.isArray() 在 ES5 就有了,兼容性很好

@Yuweiai
Copy link

Yuweiai commented Jul 5, 2022

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]';
      };
    }

null和undefined不能直接使用toString:Cannot read properties of null/undefined

@kiiirra
Copy link

kiiirra commented Jul 20, 2022

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]';
      };
    }

null和undefined不能直接使用toString:Cannot read properties of null/undefined

那是你没有定义变量....

@yyISACoder
Copy link

  • Array.isArray()是ES5新增的方法

Array.isArray()应该是ES6新增的方法哦

@DennisYongLe
Copy link

这里在理解的时候可能会把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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests