毫无疑问,Array.isArray是现如今JavaScript中判断对象是否属于Array类型的首选,但是我认为了解本文其余的方法及其背后的原理与局限性也是很有必要的,因为在JavaScript中的大多数引用类型并没有像Array类型一样提供一个isArray的判断方法,此时使用其余的方法举一反三是很有必要的。

前言

毫无疑问,Array.isArray是现如今JavaScript中判断对象是否属于Array类型的首选,但是我认为了解本文其余的方法及其背后的原理与局限性也是很有必要的,因为在JavaScript中的大多数引用类型并没有像Array类型一样提供一个isArray的判断方法,此时使用其余的方法举一反三是很有必要的。

鸭子模型

当一只动物走起路来像鸭子,叫起来也像鸭子,那它就是一只鸭子。
当一个对象中包含Array类型中的属性(例如‘splice’、‘join’或者‘length’)时,那它就属于Array类型。

prototypejs的1.6.0.3版本就是使用的这个逻辑,代码如下:

isArray: function(object) {    return object != null && typeof object == "object" && 'splice' in object && 'join' in object;}

不过鸭子模式存在一个问题,当一只动物走起路来像鸭子,叫起来也像鸭子时,它除了可能是鸭子外,还可能是‘唐老鸭’。

这就好比一个包含Array类型中的属性‘splice’、‘join’两个属性的对象,它除了可能是Array类型外,还可能是Person类型。

function isArray(object) {    return object != null && typeof object == "object" && 'splice' in object && 'join' in object;}function Person(){    /**    code    */}Person.prototype.splice = function(){    /**    code    */}Person.prototype.join = function(){    /**    code    */}let p = new Person();let isArr = isArray(p);console.log('isArray : ' + isArr);//isArray : true

鸭子模式更多用在判断‘like Array’上,比如jquery中的isArrayLike方法,代码如下:

function isArrayLike( obj ) {var length = !!obj && obj.length,type = toType( obj );if ( typeof obj === "function" || isWindow( obj ) ) {return false;}return type === "array" || length === 0 ||typeof length === "number" && length > 0 && ( length - 1 ) in obj;}

其中的 length === 0 和 typeof length === "number" && length > 0 && ( length - 1 ) in obj 两条判断逻辑皆是通过‘length’属性来判断对象是否符合‘like Array’。

instanceof关键字

关于instanceof关键字的内容在深入了解typeof与instanceof的使用场景及注意事项一文中已经详细阐述过,故在此只作简单说明。通过instanceof关键字来判断对象的原型链上是否存在函数Array的prototype属性值。如果存在,则说明此对象为Array类型,反之则不然。

不过instanceof也并不能完全可信,比如通过Symbol.hasInstance属性可以影响instanceof的判断结果:

function Person(){}Object.defineProperty(Person,Symbol.hasInstance,{    value : function(){        return false;    }})let p = new Person();p instanceof Person;//false

当数据和类型不在一个全局变量下时也可以影响instanceof的判断结果。比方说现在定义两个html文件,分别为main.html和iframe.html,代码如下:

main.html

main

iframe.html

iframeiframe

npx http-server打开main.html后,得到结果:

由此可得知:不同全局变量下的同名构造函数并不是同一个函数,当数据和类型不在同一个全局变量下时使用instanceof来判断是不可行的。

Object.prototype.toString方法

Object.prototype.toString是不会受到跨全局变量影响的。

main.html

main

iframe.html

iframeiframe

npx http-server打开main.html后,得到结果:

不过使用Symbol.toStringTag会影响Object.prototype.toString的输出。

let toString = Object.prototype.toString;function Person(){}let p = new Person();console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Object]Object.defineProperty(p,Symbol.toStringTag,{    get(){        return "Person";    }})console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Person]

也可以:

let toString = Object.prototype.toString;function Person(){}let p = new Person();console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Object]Object.defineProperty(Person.prototype,Symbol.toStringTag,{    get(){        return "Person";    }})console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Person]

还可以这样写:

let toString = Object.prototype.toString;class Person{    get [Symbol.toStringTag](){        return 'Person';    }}let p = new Person();console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Person]

Array.isArray方法

Array.isArray是可以修改的,因为它的writable属性值为true。

Object.getOwnPropertyDescriptor(Array,'isArray');//{writable: true, enumerable: false, configurable: true, value: ƒ}Array.isArray = function(data){    return null !== data && typeof data === 'object';}console.log(Array.isArray(window));//true

Array.isArray是不会受到跨全局变量影响的,并且修改Symbol.toStringTag 也不会影响到Array.isArray的判断。

let toString = Object.prototype.toString;Object.defineProperty(Array.prototype,Symbol.toStringTag,{    get(){        return "Person";    }})let arr = new Array();console.log(Array.isArray(arr));//trueconsole.log('toString.call(arr) : ' + toString.call(arr));//toString.call(arr) : [object Person]

具体Array.isArray的判断逻辑  我找到了v8中的array-isarray.tq文件。

// Copyright 2019 the V8 project authors. All rights reserved.// Use of this source code is governed by a BSD-style license that can be// found in the LICENSE file.namespace runtime {  extern runtime ArrayIsArray(implicit context: Context)(JSAny): JSAny;}  // namespace runtimenamespace array {  // ES #sec-array.isarray  javascript builtin ArrayIsArray(js-implicit context:                                      NativeContext)(arg: JSAny): JSAny {    // 1. Return ? IsArray(arg).    typeswitch (arg) {      case (JSArray): {        return True;      }      case (JSProxy): {        // TODO(verwaest): Handle proxies in-place        return runtime::ArrayIsArray(arg);      }      case (JSAny): {        return False;      }    }  }}  // namespace array

结尾

由于本人才疏学浅,如发现问题,希望能向本人指出,感谢。

©著作权归作者所有:来自51CTO博客作者wx60877cc7b61d6的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. JavaScript中的基本字符串与字符串对象的区别
  2. msqly
  3. 两集合比较判断
  4. 关于Go语言,你可能会讨厌的五件事
  5. MySQL字符类型datetime与timestamp
  6. mysql中生成列与JSON类型的索引
  7. mysql的数据类型和字符集
  8. 商家寄件运力接口-查询全国快递公司运力覆盖情况的案例代码
  9. 留言板添加字数实时统计和超出判断以及数组字符串方法

随机推荐

  1. android兼容小米xiaomi刘海屏解决方案
  2. android adb
  3. Android TextView设置跑马灯无效?
  4. android 启动过程深入解析
  5. Android核心分析 之十一-------Android G
  6. Android开发实战二之Hello Android实例
  7. Android(安卓)2.2兼容性移植
  8. android init 进程分析 (1 简介)
  9. Android五大基本组件
  10. Android(安卓)快速开发系列 ORMLite 框架