study icon indicating copy to clipboard operation
study copied to clipboard

Javascript伪数组解析

Open 24wangchen opened this issue 10 years ago • 4 comments

简介

js没有真正的数组,因此typeof不能识别数组与对象。用constructor检测在不同帧和窗口创建的数组会给出false,所以应当使用Array.prototype.toString.call(arr) === "[object Array]"。常见的伪数组有arguments,通过调用getElementsByTagName、document.childNodes等方法得到的NodeList对象。

实现一个伪数组

var pseudoArr = {0:'a', 1:'b', length: 2};
var arr = Array.prototype.slice.call( pseudoArr );
console.log( arr );//["a","b"]

//下标没有对应的值
var pseudoArr1 = {a:'a', b:'b', length: 2};
var arr1 = Array.prototype.slice.call( pseudoArr1 );
console.log( arr1 );//[undefined x 2];
console.log( arr1[0] );//undefined

//length不是数值
var pseudoArr2 = {0:'a', 1:'b', length: 'num'};
var arr2 = Array.prototype.slice.call( pseudoArr2 );
console.log( arr2 );//[];
console.log( arr2[0] );//undefined

slice内部原理

pseudoArr1、pseudoArr2均转化为了数组,但是数组中的值为undefined,查看V8引擎的array.js源码

function ArraySlice(start, end) {
  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
  var array = TO_OBJECT_INLINE(this);
  var len = TO_UINT32(array.length);
  var start_i = TO_INTEGER(start);
  var end_i = len;
  if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
  if (start_i < 0) {
    start_i += len;
    if (start_i < 0) start_i = 0;
  } else {
    if (start_i > len) start_i = len;
  }
  if (end_i < 0) {
    end_i += len;
    if (end_i < 0) end_i = 0;
  } else {
    if (end_i > len) end_i = len;
  }
  var result = [];
  if (end_i < start_i) return result;
  if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
    %NormalizeElements(array);
    %NormalizeElements(result);
    SparseSlice(array, start_i, end_i - start_i, len, result);
  } else {
    SimpleSlice(array, start_i, end_i - start_i, len, result);
  }
  result.length = end_i - start_i;
  return result;
}
var array = TO_OBJECT_INLINE(this);
var len = TO_UINT32(array.length);

这段代码可以看出slice不需要this为array类型,只需要有length属性即可。并且当length不为数字时,TO_UINT32(array.length)返回0。 由此得出结论:pseudoArr1转化为length为2的数组,其值初始化为undefined;pseudoArr2转化为length为0的数组,其值自然为undefined。

PS:chrome、FF下使用console.log打印jQuery中$(),结果为[]。此处的原因在于如果一个对象内部属性存在length和splice方法,那么就会打印出[]。这可能是由于鸭式辨型的缘故,如果一个对象存在length及splice方法,那么就认为它是一个数组。(此处待考证)

var a = {length: 0, splice: function(){}};
a;//[]

24wangchen avatar Apr 15 '15 07:04 24wangchen

star

arjenhill avatar Apr 16 '15 02:04 arjenhill

:100: :+1:

zhoufenfens avatar Apr 17 '15 15:04 zhoufenfens

好文章

tm-roamer avatar Apr 18 '15 22:04 tm-roamer

hkongm avatar Apr 20 '15 07:04 hkongm