前言

毫无疑问,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

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>main</title>
</head>
<body>
<iframe src="./iframe.html"></iframe>
<script type="text/javascript">
window.onload = function(){
console.log('document.domain : ' + document.domain);
let mainArr = [1,2,3,4,5];
console.log('mainArr instanceof Array : ' + (mainArr instanceof Array));//
let iframe = document.documentElement.getElementsByTagName('iframe')[0];
let iframeWin = iframe.contentWindow.window;
let iframeArr = iframeWin.arr;
console.log('iframeArr : ' + JSON.stringify(iframeArr));
console.log('iframeArr instanceof Array : ' + (iframeArr instanceof Array));//
console.log('iframeArr instanceof iframeWin.Array : ' + (iframeArr instanceof iframeWin.Array));//
}
</script>
</body>
</html>

iframe.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>iframe</title>
</head>
<body>
<p>iframe</p>
<script type="text/javascript">
window.onload = function(){
window.arr = [6,7,8,9,10];
}
</script>
</body>
</html>

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

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

Object.prototype.toString方法

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

main.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>main</title>
</head>
<body>
<iframe src="./iframe.html"></iframe>
<script type="text/javascript">
window.onload = function(){
let toString = Object.prototype.toString;
console.log('document.domain : ' + document.domain);
let mainArr = [1,2,3,4,5];
console.log('toString.call(mainArr) : ' + (toString.call(mainArr)));//
let iframe = document.documentElement.getElementsByTagName('iframe')[0];
let iframeWin = iframe.contentWindow.window;
let iframeArr = iframeWin.arr;
console.log('toString.call(iframeArr) : ' + (toString.call(iframeArr)));//
}
</script>
</body>
</html>

iframe.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>iframe</title>
</head>
<body>
<p>iframe</p>
<script type="text/javascript">
window.onload = function(){
window.arr = [6,7,8,9,10];
}
</script>
</body>
</html>

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));//true console.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 runtime namespace 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

结尾

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

JavaScript中判断对象是否属于Array类型的4种方法及其背后的原理与局限性的更多相关文章

  1. JavaScript实现判断图片是否加载完成的3种方法整理

    JavaScript实现判断图片是否加载完成的3种方法整理 有时候我们在前端开发工作中为了获取图片的信息,需要在图片加载完成后才可以正确的获取到图片的大小尺寸,并且执行相应的回调函数使图片产生某种显示 ...

  2. JavaScript中判断对象类型方法大全1

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

  3. JavaScript中判断对象类型的种种方法

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

  4. 转 JavaScript中判断对象类型的种种方法

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

  5. Javascript 中判断对象为空

    发现了一个巧妙的实现: 需要检查一个对象(Object)是否为空,即不包含任何元素.Javascript 中的对象就是一个字典,其中包含了一系列的键值对(Key Value Pair).检查一个对象是 ...

  6. js -【 数组】判断一个变量是数组类型的几种方法

    怎么判断一个数组是数组呢? 其实这个也是一个常考的题目.依稀记得我为数不多的面试经过中都被问道过. 方案一: instanceof variable instanceof Array 解决思路: 使用 ...

  7. JavaScript中判断对象类型方法大全2

    在JavaScript中,有5种基本数据类型和1种复杂数据类型,基本数据类型有:Undefined, Null, Boolean, Number和String:复杂数据类型是Object,Object ...

  8. javascript中判断对象类型

    <script type="text/javascript"> //判别一个对象属性在不在某个对象中 //in 是用于查找某个属性是否存在于对象中,它会把对象 //里面 ...

  9. JavaScript中unicode编码与String互转(三种方法)

    1.引言 JS本身就支持unicode转string功能,一共有三种方式和String单个字符转unicode编码. 2.方法 //unicode转String 1. eval("'&quo ...

随机推荐

  1. Spring Boot构建 RESTful 风格应用

    Spring Boot构建 RESTful 风格应用 1.Spring Boot构建 RESTful 风格应用 1.1 实战 1.1.1 创建工程 1.1.2 构建实体类 1.1.4 查询定制 1.1 ...

  2. vulnhub靶场之AI-WEB1.0渗透记录

    在本机电脑上自行搭建了一个练手的靶场,下面是记录渗透过程 目录 一.确认靶机ip 二.端口&目录扫描 三.查看敏感目录 四.sql注入 五.get shell 六.系统提权 确认靶机ip ka ...

  3. Centos7安装成功后,网卡配置及更改镜像地址为国内镜像

    Centos7安装成功后,网卡配置及更改镜像地址为国内镜像 一.网卡配置 二.修改网络配置 踩坑一:IPADDR 踩坑二:网关,DNS与本地不一致 重启网络服务 三.镜像修改为aliyun 四.相关知 ...

  4. CS229 Lecture 02

    最近忙成狗,各种意义上.第二章其实之前已经看过了但是已经完全忘记了,于是重新看了一遍当复习. 判别学习算法:直接学习$p(y|x)$,或学习一个假设$h_{\theta}(x)$输出结果 生成学习算法 ...

  5. 【LAMP】搭建Web网站过程中的记录【Ubuntu18.04+Apache2.4+PHP7.2+MySQL5.7】

    全文使用的环境如题,主机使用的是腾讯云主机. 内容应该会是linux和apache这些所有部分都有一点,因为是遇见一个问题就记录一个. 配置LAMP环境 这部分可以参考这篇文章:https://www ...

  6. spark提交命令 spark-submit 的参数 executor-memory、executor-cores、num-executors、spark.default.parallelism分析

    转载:https://blog.csdn.net/zimiao552147572/article/details/96482120 nohup spark-submit --master yarn - ...

  7. Eclipse配置MySQL连接工具

    1.项目名称右键新建文件夹lib 2.用鼠标将mysql-connector-java-5.1.15-bin.jar移动到lib文件夹中 3.选择Copy files点击OK 4.右键移动过来的mys ...

  8. 2019牛客暑期多校训练营(第七场)E-Find the median(思维+树状数组+离散化+二分)

    >传送门< 题意:给n个操作,每次和 (1e9范围内)即往数组里面插所有 的所有数,求每次操作后的中位数思路:区间离散化然后二分答案,因为小于中位数的数字恰好有个,这显然具有单调性.那么问 ...

  9. MIT 6.S081 聊聊xv6的文件系统(中)日志层与事务

    前言 我本想把上篇中没讲完的剩余层全部在本篇中讲完,但没想到越写越多.日志层的代码不多,其思想和解决问题的手段也不算难以理解,但其背后涉及的原理和思想还是非常值得回味的,因此我打算用一整篇完整的blo ...

  10. C# 网络加密与解密

    数据在网络传输过程中的保密性是网络安全中重点要考虑的问题之一.由于通过网络传递数据是在不安全的信道上进行传输的,因此通信双方要想确保任何可能正在侦听的人无法理解通信的内容,而且希望确保接收方接收的信息 ...