你的位置:首页 > Java教程

[Java教程]js与jquery常用数组方法总结


  昨天被问数组方法的时候,问到sort()方法是否会改变原来的数组。本来我猜是不会,也是这么说,马上我又觉得,知识这种东西,不确定的时候直接说不确定或不知道就好,只是凭借着不确定的猜测或者是记忆,害人害己,于是我又回答不知道。结果果然。。。我记错了0.0 还是自己总结测试一下比较靠谱,印象也比较深刻。欢迎大家提出相关意见或建议,提前谢谢哈~

  一.原生js方法

  1.遍历数组 in(同时也是对象遍历属性的方法)

var a = [1, 2, 3];for (x in a) {  console.log(x);}

 

  2.合并数组 concat

var a = [1, 2, 3],  b = [4, 5, 6],  c;c = b.concat(a);   //将a加在b上,返回新数组,a和b并没有变。参数数量不限console.log(b);console.log(c);

 

 

  3.合并数组的值为字符串 join

var a = [1, 2, 3],  b = a.join('*'); //默认为之间加上 ,     console.log(a);   //a并没有变console.log(b);

  参数为分隔符,默认为" , "分隔,当参数为' '时表示直接连接,常用于js中拼接html,如自定义弹窗,js生成form表单。

 

  4.数组排序 sort

var a = [6, 2, 3, 'a', 'x', 20],  b = a.sort();     //ASC表顺序,先看首位,因此20排在3前面console.log(a);      //a变化了console.log(b);a.push('k');console.log(b);      //a和b指向同一个对象,b相当于a的别名

  可以在参数里写排序规则函数, 如全是数字从小到大排序(注意返回值正负)

var a = [3, 2, 6, 20],  b = a.sort(function(x, y) {    return x - y;  });console.log(b);

  当不全是数字的时候,会产生奇葩错误(NaN),见下图

  

  由此可知,注意运算符的正确用法还是很重要的。对于NaN,能少操作就少操作,毕竟是唯一一个js中不等于任何值的类型(Not a Number),包括它本身(可以用来判断一个变量是不是NaN,未初始化的变量是无类型的)。

 

  5.数组的模拟栈(FILO) 和队列(FIFO) 方法(均改变原来数组)

var a = [6, 2, 3, 'a', 'x', 20],  b = a.push('ab'),  //末尾添加元素,并返回新长度  c = a.pop(),     //删除并返回数组的最后一个元素  d = a.unshift('xy'), //开头添加元素,并返回新长度  e = a.shift();    //删除并返回数组的第一个元素console.log(a);console.log(b);console.log(c);console.log(d);console.log(e);

  可见这类方法添加元素则返回添加后长度, 删除元素则返回被删的那个可怜家伙(同splice)。

 

  6.数组反序 reverse

var a = [6, 2, 3, 'a', 'x', 20],  b = a.reverse();    //返回a的引用console.log(a);console.log(b);

  

  7.取数组中需要的部分 slice

var a = [6, 2, 3, 'a', 'x', 20],  b = a.slice(0, 2);  //下标从0取到2(不包括2),没有第二个参数则默认到末尾。第一个参数为负表示从末尾开始数。第一个参数小于第二个参数则为空。console.log(a);console.log(b);      //b是a一部分的副本,a本身不变

 

  8.修改数组 splice (既然是修改数组,肯定数组本身会变的啦)

var a = [1, 2, 3, 4],  b = a.splice(0, 2, 6);console.log(a);     console.log(b);     //b为被删掉的数组部分

  a.splice(index, num, newItem1, newItem2...):index为开始选择的元素下标,num为接下来要删除元素的个数,newItem为接下来(在删完的地方)要添加的新元素(非必须)。这个方法用途最多,如

  删除指定下标(2,也就是第三个)元素,此时不设置需要添加的newItem,而num设为1

var a = [1, 2, 3, 4],  b = a.splice(2, 1);console.log(a);console.log(b); 

  在任意位置添加任意多个元素(如在下标2后添加两个元素'7','8'),此时num设为0

var a = [1, 2, 3, 4],  b = a.splice(2, 0, 7,8);console.log(a);console.log(b);  //没有删除,b返回[]

  根据元素值删除元素(结合jquery)

var a=[1,2,3,4];a.splice($.inArray(2,a),1);console.log(a);

 

  plus:数组也是特殊的对象(但是拥有自己的方法,一般用下标访问),因此同样也有对象的通用方法toString和valueOf

var a = [1, 2, [3,2], 4],  b = a.toString(); // 转化为字符串(不管有多少层)console.log(a);    //a本身不变console.log(b); 

var a = [1, 2, 4],  b = a.valueOf(); // 返回原始值(其实还是它本身。。。)console.log(a);    //a本身不变console.log(b); 

 

  小结:综上所述,js数组的原生方法里面

  修改自身的有: splice, pop, push, shift, unshift, sort, reverse

  不修改自己身的: slice, concat, join

 

  二.Jquery常用js方法

  1.遍历

  可以对所有的元素进行操作。如果想要满足条件退出,用return false( 绝大部分jquery方法都可以这么退出)。 

  $.each(arr, callback(key, val));      //可以链式调用,返回arr,为本身

var a = [1, 2, 3, 4];$.each(a, function(key, val) {   //以jQuery对象的方法调用,兼容性好;也可以用$(a)将a转化为jquery对象,然后以$(a).each(function(){})的形式调用,下面的方法同  console.log(a[key] + '下标为' + key + '值为' + val);});

//对应源码(jquery1.11.0下面同)

// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.

each: function( obj, callback, args ) {    var value,      i = 0,      length = obj.length,      isArray = isArraylike( obj );    if ( args ) {      if ( isArray ) {        for ( ; i < length; i++ ) {          value = callback.apply( obj[ i ], args );  //第三个参数用于扩展obj元素的方法,一般不用          if ( value === false ) {            break;          }        }      } else {        for ( i in obj ) {          value = callback.apply( obj[ i ], args );          if ( value === false ) {            break;          }        }      }    // A special, fast, case for the most common use of each    } else {      if ( isArray ) {        for ( ; i < length; i++ ) {          value = callback.call( obj[ i ], i, obj[ i ] );          if ( value === false ) {            break;          }        }      } else {        for ( i in obj ) {          value = callback.call( obj[ i ], i, obj[ i ] );          if ( value === false ) {            break;          }        }      }    }    return obj;  }

 

  2. 筛选

  $.grep(arr, callback, invert)

  invert为false表示对callback的筛选取反。 默认为true。

var a = [1, 2, 3, 4];$.grep(a, function(val, key) { //不能链式调用,返回[],所以可以加上return实现链式,返回满足条件的副本  if (a[key] > 2) {    console.log(key);  }  return val;});

  常用做获取两个数组中相同(或不相同)的部分

var a= [1, 2, 3, 4],  b=[1,3,5,7];$.grep(a,function(val,key){  if(b.indexOf(val)>=0){    return val;  }},false);

//jquery源码grep: function( elems, callback, invert ) {    var callbackInverse,      matches = [],      i = 0,      length = elems.length,      callbackExpect = !invert;    // Go through the array, only saving the items    // that pass the validator function    for ( ; i < length; i++ ) {      callbackInverse = !callback( elems[ i ], i );  //如果callback没有设置return,那么返回undefined(!undefined还是undefined)      if ( callbackInverse !== callbackExpect ) {        matches.push( elems[ i ] );     //只添加满足条件的,内部实现为push方法      }    }    return matches;  }

 

  3.转换

  $.map(arr,callback(key,val))

var a = [1, 2, 3, 4];$.map(a, function(val, key) { //不能链式调用,返回[],同grep加上return即可放回副本  if (a[key] > 2) {    a[key]=val+1;  }  return val;       //可以链式调用,返回处理后的数组(也可用于筛选)});

// arg is for internal usage only  map: function( elems, callback, arg ) {    var value,      i = 0,      length = elems.length,      isArray = isArraylike( elems ),      ret = [];    // Go through the array, translating each of the items to their new values    if ( isArray ) {      for ( ; i < length; i++ ) {        value = callback( elems[ i ], i, arg );        if ( value != null ) {          ret.push( value );        }      }    // Go through every key on the object,    } else {      for ( i in elems ) {        value = callback( elems[ i ], i, arg );  //如果callback没有返回值,那么value就是undefined        if ( value != null ) {          ret.push( value );        }      }    }    // Flatten any nested arrays    return concat.apply( [], ret );          //如果callback没有返回值,那么value就是[]
}

  背景标白的区域为与each方法不同的地方,可以简单的理解为返回对象是否是副本(map是副本),另外map是为数组或类数组对象量身定做的,而each可以应用于全部对象。

 

  4.合并

  $.merge(arr1,arr2)  arr1后面加上arr2后返回arr1

var a=[1,2,3],  b=[4,5,6];$.merge(a,b);       //可以有多个参数(居然不报错!),但是第三个及以后的没用(test in FF and Chrome)

//jquery源码merge: function( first, second ) {    var len = +second.length,       j = 0,      i = first.length;    while ( j < len ) {      first[ i++ ] = second[ j++ ];    }    // Support: IE<9    // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)    if ( len !== len ) {      while ( second[j] !== undefined ) {        first[ i++ ] = second[ j++ ];      }    }    first.length = i;    return first;  }

  源码的第二行有个+,最开始我以为是显式声明非负值,后来看到了arraylike,于是测试了极端情况如下:

var ax;ax.length    //报错,type error+ax.length    //报错,type errorvar ab={};    //空对象作为类空数组对象ab.length    //undefined+ab.length    //NaNvar ab=[];ab.length    //0+ab.length    //0var ab=null;ab.length     //报错,type error+ab.length    //报错,type error

  忽然觉得然并卵。。。好吧,也许是我计较太多。如果有谁看到有关的解释,麻烦留下言,谢谢~

 

  5.过滤相同元素

  $.unique(arr)//过滤Jquery对象数组中重复的元素(内部实现为===)(不同版本不一样,不要用)

var a = [ 1 , 1 , 2 , 3 , 7 , 4 , 5 , 5 , 6 , 6 ];$.unique(a)

  jquery1.11.0运行结果

  jquery1.8.3运行结果

  好神奇啊,有木有!看一下源码保险~

//jquery1.11.0jQuery.unique = Sizzle.uniqueSort;Sizzle.uniqueSort = function( results ) {  var elem,    duplicates = [],    j = 0,     i = 0;  // Unless we *know* we can detect duplicates, assume their presence  hasDuplicate = !support.detectDuplicates;   sortInput = !support.sortStable && results.slice( 0 );  results.sort( sortOrder );  if ( hasDuplicate ) {    while ( (elem = results[i++]) ) {      if ( elem === results[ i ] ) {     //用===        j = duplicates.push( i );      }    }    while ( j-- ) {      results.splice( duplicates[ j ], 1 );  //用splice实现    }  }  // Clear input after sorting to release objects  // See https://github.com/jquery/sizzle/pull/225  sortInput = null;  return results;};

//jquery1.8.3jQuery.unique = Sizzle.uniqueSort;Sizzle.uniqueSort = function( results ) {  var elem,    duplicates = [],    i = 1,    j = 0;  hasDuplicate = baseHasDuplicate;  results.sort( sortOrder );  if ( hasDuplicate ) {    for ( ; (elem = results[i]); i++ ) {      if ( elem === results[ i - 1 ] ) {        j = duplicates.push( i );      }    }    while ( j-- ) {      results.splice( duplicates[ j ], 1 );    }  }  return results;};

  对应红色的字体为新增或者修改的,然而并看不出什么,调试一下进入,会发现问题居然在sortOrder上!坑啊!继续找~~~

//jquery1.11.0//定义时sortOrder = function(a, b) {  if (a === b) {    hasDuplicate = true;  }  return 0;};//setDocument里面sortOrder = hasCompare ?  function(a, b) {    // Flag for duplicate removal    if (a === b) {      hasDuplicate = true;      return 0;    }    // Sort on method existence if only one input has compareDocumentPosition    var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;    if (compare) {      return compare;    }    // Calculate position if both inputs belong to the same document    compare = (a.ownerDocument || a) === (b.ownerDocument || b) ?      a.compareDocumentPosition(b) :      // Otherwise we know they are disconnected      1;    // Disconnected nodes    if (compare & 1 ||      (!support.sortDetached && b.compareDocumentPosition(a) === compare)) {      // Choose the first element that is related to our preferred document      if (a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a)) {        return -1;      }      if (b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b)) {        return 1;      }      // Maintain original order      return sortInput ?        (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) :        0;    }    return compare & 4 ? -1 : 1;  } :  function(a, b) {    // Exit early if the nodes are identical    if (a === b) {      hasDuplicate = true;      return 0;    }    var cur,      i = 0,      aup = a.parentNode,      bup = b.parentNode,      ap = [a],      bp = [b];    // Parentless nodes are either documents or disconnected    if (!aup || !bup) {      return a === doc ? -1 :        b === doc ? 1 :        aup ? -1 :        bup ? 1 :        sortInput ?        (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) :        0;      // If the nodes are siblings, we can do a quick check    } else if (aup === bup) {      return siblingCheck(a, b);    }    // Otherwise we need full lists of their ancestors for comparison    cur = a;    while ((cur = cur.parentNode)) {      ap.unshift(cur);    }    cur = b;    while ((cur = cur.parentNode)) {      bp.unshift(cur);    }    // Walk down the tree looking for a discrepancy    while (ap[i] === bp[i]) {      i++;    }    return i ?      // Do a sibling check if the nodes have a common ancestor      siblingCheck(ap[i], bp[i]) :      // Otherwise nodes in our document sort first      ap[i] === preferredDoc ? -1 :      bp[i] === preferredDoc ? 1 :      0;  };

View Code in jquery1.11.0
//jquery 1.8.3sortOrder = docElem.compareDocumentPosition ?  function( a, b ) {    if ( a === b ) {      hasDuplicate = true;      return 0;    }    return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?      a.compareDocumentPosition :      a.compareDocumentPosition(b) & 4    ) ? -1 : 1;  } :  function( a, b ) {    // The nodes are identical, we can exit early    if ( a === b ) {      hasDuplicate = true;      return 0;    // Fallback to using sourceIndex (in IE) if it's available on both nodes    } else if ( a.sourceIndex && b.sourceIndex ) {      return a.sourceIndex - b.sourceIndex;    }    var al, bl,      ap = [],      bp = [],      aup = a.parentNode,      bup = b.parentNode,      cur = aup;    // If the nodes are siblings (or identical) we can do a quick check    if ( aup === bup ) {      return siblingCheck( a, b );    // If no parents were found then the nodes are disconnected    } else if ( !aup ) {      return -1;    } else if ( !bup ) {      return 1;    }    // Otherwise they're somewhere else in the tree so we need    // to build up a full list of the parentNodes for comparison    while ( cur ) {      ap.unshift( cur );      cur = cur.parentNode;    }    cur = bup;    while ( cur ) {      bp.unshift( cur );      cur = cur.parentNode;    }    al = ap.length;    bl = bp.length;    // Start walking down the tree looking for a discrepancy    for ( var i = 0; i < al && i < bl; i++ ) {      if ( ap[i] !== bp[i] ) {        return siblingCheck( ap[i], bp[i] );      }    }    // We ended someplace up the tree so do a sibling check    return i === al ?      siblingCheck( a, bp[i], -1 ) :      siblingCheck( ap[i], b, 1 );  };

View Code in jquery 1.8.3

  很多是不是?有木有觉得瞬间被我坑了?啊哈,其实只要继续调试的时候断点设置好,你就会发现~~~没有比这更坑的了!它们都是循环的!1.8.3里面就在第一个function里面转来转去,手都点酸了也没看到出来,1.11.0整体循环,有参数的个数那么多次。

  最后的结论是:还是不要用这个不靠谱的函数了。如果需要类似的功能,用原生js手动写就好。同时说明了关注更新的重要性,不过程序猿也不一定有那么长的时间去关注每一次的更新,那么就一定要准确的了解自己手头的版本,最好是空闲时对用到的功能测试一下,看下源码,网上得来终觉浅啊~

 

  6.判断

  $.inArray(val,arr)  判断val是否在arr里面

var a = [1, 2, 3, 4];$.inArray(2, a);   //有的话返回下标,没有的话返回-1

//对应源码    inArray: function(elem, arr, i) {      var len;      if (arr) {        if (indexOf) {          return indexOf.call(arr, elem, i);   //实际实现        }        len = arr.length;        i = i ? i < 0 ? Math.max(0, len + i) : i : 0;        for (; i < len; i++) {          // Skip accessing in sparse arrays          if (i in arr && arr[i] === elem) {   //一般尽可能用===而杜绝==可能带来的隐式转换            return i;          }        }      }      return -1;    }

 

  7.元素转数组

  $.makeArray()将一个类数组对象转换为真正的数组对象。(所谓"类数组对象"就是一个常规的Object对象,但它和数组对象非常相似:具备length属性,并以0、1、2、3……等数字作为属性名。不过它毕竟不是数组,没有从数组的原型对象上继承下来的内置方法(例如:push()、 sort()等)。)

     $.toArray()将所有DOM元素恢复成数组。(其实用选择器取的时候就是很自然的数组的形式)
  这两个实际用得太少了就不具体分析了,知道有这玩意就行吧。  

 

  三.补充

  1.清空数组

  方法1:length设为0 (js身为弱变量类型语言的体现之一,array的length属性可写)(效率比较低)

  方法2:直接指向[]的引用 (如闭包销毁指向null一样,垃圾回收会自动回收空间)(效率比较高)

 

  2.兼容性  

  IE8下
  $.inArray 代替 indexOf
  $.grep代替Array.prototype.filter

 

  3.注意事项

  一般情况下用$.functionName(obj,callback)的形式调用jquery方法的兼容性比较好,比如我曾遇到IE8及以下不能识别$(dom).val().trim()的情况,显示trim is not a function,然而改为$.trim($(dom).val())就没问题。

  前面那种情况实际是string调用trim方法(如果加上call或者apply注入trim方法可以过,但是必要嘛?),后面是jquery对象调用trim方法。

  最后再啰嗦一句,欢迎大家的意见和建议,帮我纠错,共同进步,谢谢!