JavaScript学习笔记:检测数组方法

很多时候我们需要对JavaScript中数据类型( Function 、 String 、 Number 、 Undefined 、 Boolean 和 Object )做判断。在JavaScript中提供了typeof操作符可以对这些常用的数据类型做判断。但要使用typeof来判断数据是不是一个数组,就不起作用了。那在实际生产中要如何来检测数据是不是一个数组呢?

今天的学习任务就是如何来检测一个数据是不是数组?

typeof操作符

typeof 可以解决大部分数据类型的检测,如:

 1 console.log(typeof function () {return;}); // function
 2 console.log(typeof "a"); // string
 3 console.log(typeof 123); // number
 4 console.log(typeof a); //undefined
 5 console.log(typeof true); // boolean
 6 console.log(typeof NaN); // number
 7 console.log(typeof !NaN); //boolean
 8 console.log(typeof {name:"大漠",age: "37"}); // object
 9 console.log(typeof ["大漠","37"]); // object
10 console.log(typeof null); // object

上面的代码示例告诉我们,typeof {name:"大漠",age: "37"}typeof ["大漠","37"]返回的都是object。事实再次证明typeof没办法对数组进行检测,那么这里引出一个问题?如何判断数据是个数组类型?

检测数组方法

虽然typeof操作符无法检测数组,但事实上检测数组方法有很多种。@Tom、@John、@Rick、@Ken和@Eric在Quora的一篇文章中总结了五种不同的检测数组的方法。接下来我们一起来了解和学习这几种检测数组的方法。

ECMAScript 5的isArray函数

1 function isArray(obj) {
2     return Array.isArray(obj);
3 }
4 var arr = ["大漠","w3cplus"]; //创建一个数组
5 isArray(arr); // true

毫无疑问,这看起来最完美的解决方案,因为他是原生的。ECMAScript 5将Array.isArray()引入JavaScript。但其兼容性令你会感到些许的失望:IE9+、 Firefox 4+、Safari 5+、Opera 10.5+和Chrome都实现了这个方法,但是在IE8之前的版本是不支持的。

在这个基础上对构造函数做一下检测,而且这个检测过程非常的快,而且也非常的准确。事实上对我们的使用太准确了。但在工作是不能确定一个变量是继承自一个数组。这样一来,在某种程度上对构造函数做检测对于我们自己来说是很需要的,也是非常有益的:

1 function isArray(obj) {
2     return (typeof obj !== ‘undefined‘ && obj && obj.constructor === Array);
3 }

对象自身的constructor属性

上面的示例中,检测构造函数时使用了对像自身的constructor属性。其实constructor属性返回一个指向创建了该对象原型的函数引用。使用该属性也可以检测数组类型。

1 var arr = ["大漠","W3cplus"];
2 console.log(arr.constructor === Array); // true

instanceof操作符

除了使用对像自身的 constructor 属性检测一个数组之外,还可以使用 instanceof 操作符来检测一个数组。

instanceof 操作符可以用来判断某个构造函数的  prototype  属性是否存在另外一个要检测对象的原型链上。也就是判断instanceof前面的对象是否是后面的类或对象的实例。

注:这个操作符和JavaScript中面向对象有点关系,了解这个就先得了解JavaScript中的面向对象。

来回忆下 instanceof 运算符的使用方式。a instanceof b,如果返回 true,表示 a 是 b 的一个实例。那么如果 a instanceof Array 返回 true,是不是就说明 a 是 数组类型呢?

1 var arr = ["大漠","W3cplus"];
2 console.log(arr instanceof Array); // true

frame实例化对象带来的问题

constructor 和 instanceof 貌似很好的两个检测数组的方法,但实际上还是有些漏洞的,当你在多个frame中回来跳的时候,这两种方法就惨了。由于每一个frame都有自己的一套执行环境,跨frame实例化的对象彼此并不共享原型链,通过instanceof操作符和constructor属性检测的方法自然会失败。

1 // 创建iframe并添加到DOM中
2 var iframe = document.createElement(‘iframe‘); //创建iframe
3 document.body.appendChild(iframe); //将创建的iframe添加到body中
4 otherArray = window.frames[window.frames.length - 1].Array;
5 var arr = new otherArray("大漠","W3cplus"); //声明数组["大漠","W3cplus"]
6 console.log(arr instanceof Array);        // false
7 console.log(arr.constructor === Array);   // false

对象原生toString检测

Object.prototype.toString 的行为:首先,取得对象的一个内部属性 [[Class]] ,然后依据这个属性,返回一个类似于 "[object Array]" 的字符串作为结果(看过ECMA标准的应该都知道,[[]]用来表示语言内部用到的、外部不可直接访问的属性,称为“内部属性”)。利用这 个方法,再配合call,我们可以取得任何对象的内部属性 [[Class]] ,然后把类型检测转化为字符串比较,以达到我们的目的。

1 isArray = function(obj) {
2     return Object.prototype.toString.call(obj) == "[object Array]";
3 }
4 var arr = ["大漠","W3cplus"];
5 console.log(isArray(arr)); // true

call 改变 toString 的this引用为待检测的对象,返回此对象的字符串表示,然后对比此字符串是否是 [object Array] ,以判断其是否是Array的实例。为什么不直接o.toString()?嗯,虽然Array继承自Object,也会有toString方法,但是这个方法有可能会被改写而达不到我们的要求,而 Object.prototype 则是老虎的屁股,很少有人敢去碰它的,所以能一定程度保证其“纯洁性”:)

JavaScript 标准文档中定义: [[Class]] 的值只可能是下面字符串中的一个:ArgumentsArrayBooleanDate,ErrorFunctionJSONMathNumberObjectRegExpString

其它方法

除了上面介绍的一些检测数组的方法之外,还有:

@Rick Waldron提供的:

 1 var arr = [1,2,3];
 2 function isArray( arg ) {
 3   if ( typeof arg === "object" &&
 4      ( "join" in arg && typeof arg.join === "function" ) &&
 5      ( "length" in arg && typeof arg.length === "number" ) ) {
 6         return true;
 7   }
 8   return false;
 9 }
10 console.log(true, isArray(arr)); // true true
11 console.log(false, isArray({join: true}) ); // false false
12 console.log(false, isArray({join: function () {return false;}}) ); // false false

@Shamasis Bhattacharya 提供的:

 1 var isArray = function (subj) {
 2     try {
 3         subj && (subj.length = -1);
 4         return false;
 5     }
 6     catch (er) {
 7         return true;
 8     }
 9 };
10 var arr = [1,2,3];
11 isArray(arr); // true

道格拉斯提供的:

1 var is_array = function (value) {
2     return value &&
3         typeof value === ‘object‘ &&
4         typeof value.length === ‘number‘ &&
5         typeof value.splice === ‘function‘ &&
6         !(value.propertyIsEnumerable(‘length‘));
7 };
8 var arr = [1,2,3];
9 is_array(arr); // true

最佳检测方法

其实也没有什么是最佳检测方法,只有最合适的检测方法。综合上面各种检测数组的方法,稍做一些处理:

 1 var isArray = (function () {
 2     if (Array.isArray) {
 3         return Array.isArray;
 4     }
 5     var objectToStringFn = Object.prototype.toString,
 6         arrayToStringResult = objectToStringFn.call([]);
 7
 8     return function (subject) {
 9         return objectToStringFn.call(subject) === arrayToStringResult;
10     };
11 }());
12
13 var arr = [];
14 isArray(arr); // true

最优化的方法就是不管Array.isArray‘是否能用,都可以回到对象原生toString检测和对象原生toString`检测上。

时间: 2024-12-13 01:41:47

JavaScript学习笔记:检测数组方法的相关文章

JavaScript学习笔记:数组reduce()和reduceRight()方法

很多时候需要累加数组项的得到一个值(比如说求和).如果你碰到一个类似的问题,你想到的方法是什么呢?会不会和我一样,想到的就是使用for或while循环,对数组进行迭代,依次将他们的值加起来.比如: var arr = [1,2,3,4,5,6]; Array.prototype.sum = function (){ var sumResult = 0; for (var i = 0; i < this.length; i++) { sumResult += parseInt(this[i]);

JavaScript学习笔记——4.数组

1.数组的作用 批量处理数据 2.声明数组 var arr=[元素1,元素2,元素3...] //快速声明 arr[1] //调用元素 var arr=new Array(元素1,元素2,元素3); //使用Array对象方式声明 3.遍历数组 for循环 4.数组处理函数 .length //数组长度(属性) .sort() //排序(可有回调函数) .push() //在数组后面加入元素(入栈) .pop() //移除数组后面的元素(出栈\栈为后进先出) .unshift() //在数组前面

JavaScript学习笔记【3】数组、函数、服务器端JavaScript概述

笔记来自<JavaScript权威指南(第六版)> 包含的内容: 数组 函数 服务器端JavaScript概述 数组 数组是动态的:根据需要它们会增长或缩减,并且在创建数组时无须声明一个固定的大小或在数组大小变化时无须重新分配空间. 数组可能是稀疏的:索引不一定要连续的,它们之间可以有空缺. 通常,数组的实现是经过优化的,用数字索引来访问数组元素一般来说比访问常规的对象属性要快很多. 数组继承自Array.prototype中的属性,它定义了一套丰富的数组操作方法. 如果省略数组直接量中的某个

JavaScript学习笔记第一天——字符串连接的N种方法

Javascript 字符串的连接方法有多种,但效率却有很大区别. 1.逐个字符串相加 var arr = ['item 1', 'item 2', 'item 3', ...]; var list = ''; for (var i = 0, l = arr.length; i < l; i++) { list += '<li>' + arr[i] + '</li>'; } list = '<ul>' + list + '</ul>'; 这种最常见的,

Java程序猿的JavaScript学习笔记(9—— jQuery工具方法)

计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript学习笔记(3--this/call/apply) Java程序猿的JavaScript学习笔记(4--this/闭包/getter/setter) Java程序猿的JavaScript学习笔记(5--prototype) Java程序猿的JavaScript学习笔记(6--面向对象模拟) Java程

Java程序猿的JavaScript学习笔记(5——prototype和Object内置方法)

计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript学习笔记(3--this/call/apply) Java程序猿的JavaScript学习笔记(4--this/闭包/getter/setter) Java程序猿的JavaScript学习笔记(5--prototype) Java程序猿的JavaScript学习笔记(6--面向对象模拟) Java程

JavaScript学习笔记【2】表达式和运算符、语句、对象

笔记来自<JavaScript权威指南(第六版)> 包含的内容: 表达式和运算符 语句 对象 表达式和运算符 数组直接量中的列表逗号之间的元素可以省略,这时省略的空位会填充值undefined.元素列表末尾可以留下单个逗号,这时并不会创建一个新的值为undefined元素. 属性访问表达式,.identifier的写法只适用于要访问的属性名称是合法的标识符,并且需要知道要访问的属性的名字.如果属性名称是一个保留字或者包含空格和标识符,或是一个数字(对于数组来说),则必须使用方括号的写法.当属性

javascript学习笔记---ECMAScriptECMAScript 对象----定义类或对象

使用预定义对象只是面向对象语言的能力的一部分,它真正强大之处在于能够创建自己专用的类和对象. ECMAScript 拥有很多创建对象或类的方法. 原始的方式 因为对象的属性可以在对象创建后动态定义(后绑定),类似下面的代码: var oCar = new Object; oCar.color = "blue"; oCar.doors = 4; oCar.mpg = 25; oCar.showColor = function() { alert(this.color); };不过这里有一

javascript学习------浏览器检测

Navigator对象:浏览器对象 该对象包含了浏览器的整体信息 Navigator对象的属性: appName:返回浏览器的名称 appVersion:返回浏览器的版本号 uerAgent:返回浏览器用于HTTP请求的用户代理头的值 appCodeName:返回浏览器的代码号 platform:返回运行浏览器的操作系统或硬件平台 IE中的Navigator属性: cookieEnabled:检测浏览器是否支持Cookie,如果支持返回TRUE,不支持返回FALSE systemLanguage