js中的伪数组
js中的伪数组
原始数据-primitive data
原始数据 (原始值、原始数据类型)不是一种 object 类型并且没有自己的方法的。
在 JavaScript 中,有六种原始数据类型:string,number,boolean,null,undefined,symbol (new in ECMAScript 2015)。
除此之外其他所有的都是对象,包括函数(Function)
引用数据类型:Object(Array,Date,RegExp,Function)
所有的原始数据都是不变的(即不能被改变)。
JavaScript 中的原始值会包装为对象
除了 null 和 undefined,所有的原始值都可以有等价的、由对象包装原始值的形式表达:
- String for the string primitive.
- Number for the number primitive.
- Boolean for the Boolean primitive.
- Symbol for the Symbol primitive.
这个包裹对象的 valueOf() 方法返回原始值。
what is Array-like objects
定义:
1、拥有length属性;
2、按索引方式存储数据,也即其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解);
3、不具有数组所具有的方法,如push()、pop()等方法。但仍可以对真正数组遍历方法来遍历它们。
伪数组,就是像数组一样有 length 属性,也有 0、1、2、3 等属性的对象,看起来就像数组一样,但不是数组,比如
var fakeArray = {
length: 3,
"0": "first",
"1": "second",
"2": "third"
};
for (var i = 0; i < fakeArray.length; i++) {
console.log(fakeArray[i]);
}
// console.log(Array.prototype.join.call(fakeArray,'+'));
伪数组是一个 Object,而真实的数组是一个 Array。
fakeArray instanceof Array === false;
Object.prototype.toString.call(fakeArray) === "[object Object]";
var arr = [1,2,3,4,6];
arr instanceof Array === true;
Object.prototype.toString.call(arr) === "[object Array]"
《javascript权威指南》上给出了代码用来判断一个对象是否属于“类数组”。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 判断o是否是一个类数组对象.
// 字符串和函数有length属性,但是它们
// 可以用typeof检测将其排除。在客户端JavaScript中,DOM文本节点
// 也有length属性,需要用额外判断o.nodeType != 3 将其排除
function isArrayLike(o) {
if (o && // o 非null、undefined 等
typeof o === 'object' && // o 是对象
isFinite(o.length) && // o.length 是有限数值
o.length >= 0 && // o.length 非负数
o.length === Math.floor(o.length) && // o.length 是整数
o.length < 4294967296) // o.length < 2^32
return true; // o是类数组对象
else
return false; // 否则它不是
}
更简单的办法来判断,用 Array.isArray
Array.isArray(fakeArray) === false;
Array.isArray(arr) === true;
Common array-like object
- function内的arguments对象
(function() {
console.log(typeof arguments); // 输出 object,它并不是一个数组
}());
- 调用 getElementsByTagName ,document.childNodes之类 获得的元素集合(NodeList)
console.log(typeof document.body.childNodes); // 输出 object
Converting an array-like object to an array
Array.protype.slice.call(obj)
var fakeArray = {0:'a',1:'b',length:2}; //这是一个标准的有伪数组对象
var newArr1 = Array.prototype.slice.call(fakeArray);
//var newArr1 = Array.prototype.slice.call(fakeArray, 0); 也可
console.log(newArr[0]);//a
var newArr2 = [].slice.call(fakeArray);
console.log(newArr2[0]);//a
slice 可以用来获取数组片段,它返回新数组,不会修改原数组。
fakeArray被成功的转换成了Array对象。通过[].slice.call这种形式实现同样的效果,但以prototype的形式执行程序效率更高,同样代码也更加优美。
Array.from(fakeArray)
var newArr = Array.from(fakeArray)
[…fakeArray]
使用扩展运算符,也是 ES6 的语法
var newArr = [...fakeArray]
slice
var fakeArray01 = {a:'a',b:'b',length:2};//没有length下标对应的值
var arr01 = Array.prototype.slice.call(fakeArray01);
alert(arr01[0]);//undefined
var fakeArray02 = {0:'a',1:'b',length:'num'};//length不是数值
var arr02 = Array.prototype.slice.call(fakeArray02);
alert(arr02[1]);//undefined
同样fakeArray01和fakeArray02被转换成了真正的数组,但是数组中的值都为undefined 查看 V8 引擎 array.js 的源码,可以将 slice 的内部实现简化为:
function slice(start, end) {
var len = ToUint32(this.length), result = [];
for(var i = start; i < end; i++) {
result.push(this[i]);
}
return result;
}
可以看出,slice 并不需要 this 为 array 类型,只需要有 length 属性即可。并且 length 属性可以不为 number 类型,当不能转换为数值时,ToUnit32(this.length) 返回 0.
根据以上结论可以得出:fakeArray01被转换成了lenth为2的数组,其值都被初始化为undefined,fakeArray02被转换成了length为0的数组,自然访问下标为1的元素返回undefined
IE的问题
针对于标准浏览器slice实现已经可以解释所有的问题,但是IE在处理NodeList时出现了问题。IE中无法将NodeList转换为真正的数组,会出错。这又是为什么呢?严格说,在IE内部定义了一个抽象类Arraioid,Array和Arguments都继承与此,所以可以用slice。但DOM对象是通过COM接入到JScript的,slice检测的时候失效。
// 伪数组转化成数组
var makeArray = function(obj) {
var arr = [];
try {
arr = Array.prototype.slice.call(obj);
return arr;
}
catch (e) {
var length = obj.length;
if (!length) return false;
for (var i = 0; i < length; i++) {
arr.push(obj[i]);
}
return arr;
}
};
demo
var fakeArray = {
length: 3,
"0": "first",
//"1": "second",
"2": "third",
"3": "four",
"five": "five"
};
console.log(isArrayLike(fakeArray)); // true
var arr01 = Array.prototype.slice.call(fakeArray,0);
console.log(Array.isArray(arr01)); // true
console.log(arr01); // ["first", 2: "third"]
console.log(arr01[0]); // five
console.log(arr01[1]); // undefined
console.log(arr01[2]); // third
console.log(arr01[3]); // undefined
var fakeArray = {
length: "num",
"0": "first",
"1": "second"
};
console.log(isArrayLike(fakeArray)); // false
var arr01 = Array.prototype.slice.call(fakeArray,0);
console.log(Array.isArray(arr01)); // ture
console.log(arr01); // []