昨天被問數組方法的時候,問到sort()方法是否會改變原來的數組。本來我猜是不會,也是這么說,馬上我又覺得,知識這種東西,不確定的時候直接說不確定或不知道就好,只是憑借着不確定的猜測或者是記憶,害人害己,於是我又回答不知道。結果果然。。。我記錯了0.0 還是自己總結測試一下比較靠譜,印象也比較深刻。歡迎大家提出相關意見或建議,提前謝謝哈~
一.原生js方法
1.遍歷數組 in(同時也是對象遍歷屬性的方法)
var a = [1, 2, 3]; for (x in a) { console.log(x); }
// 0
// 1
// 2
2.合並數組 concat
var a = [1, 2, 3], b = [4, 5, 6], c; c = b.concat(a); //將a加在b后,返回新數組,a和b並沒有變。參數數量不限 console.log(b); // [4, 5, 6]
console.log(c); // [4, 5, 6, 1, 2, 3]
3.合並數組的值為字符串 join
var a = [1, 2, 3], b = a.join('*'); //默認為之間加上 , console.log(a); // [1, 2, 3] //a並沒有變 console.log(b); // 1*2*3
參數為分隔符,默認為" , "分隔,當參數為' '時表示直接連接,常用於js中拼接html,如自定義彈窗,js生成form表單。
4.數組排序 sort
var a = [6, 2, 3, 'a', 'x', 20], b = a.sort(); //ASC表順序,先看首位,因此20排在3前面 console.log(a); // [2, 20, 3, 6, "a", "x"] //a變化了 console.log(b); // [2, 20, 3, 6, "a", "x"] a.push('k'); console.log(b); // [2, 20, 3, 6, "a", "x", "k"] //a和b指向同一個對象,b相當於a的別名,所以 b 也變了
可以在參數里寫排序規則函數, 如全是數字從小到大排序(注意返回值正負)
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); // [20, "x", "a", 3, 2, 6] console.log(b); // [20, "x", "a", 3, 2, 6]
7.取數組中需要的部分 slice
var a = [6, 2, 3, 'a', 'x', 20], b = a.slice(0, 2); //下標從0取到2(不包括2),沒有第二個參數則默認到末尾。第一個參數為負表示從末尾開始數。第一個參數小於第二個參數則為空。 console.log(a); console.log(b); // [6, 2] //b是a一部分的副本,a本身不變
8.修改數組 splice (既然是修改數組,肯定數組本身會變的啦)
var a = [1, 2, 3, 4], b = a.splice(0, 2, 6); console.log(a); // [6, 3, 4] console.log(b); // [1, 2] //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); // [1, 2, 4] console.log(b); // [3]
在任意位置添加任意多個元素(如在下標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); // [1, 3, 4]
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 error var ab={}; //空對象作為類空數組對象 ab.length //undefined +ab.length //NaN var ab=[]; ab.length //0 +ab.length //0 var 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.0 jQuery.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.3 jQuery.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; };

//jquery 1.8.3 sortOrder = 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 ); };
很多是不是?有木有覺得瞬間被我坑了?啊哈,其實只要繼續調試的時候斷點設置好,你就會發現~~~沒有比這更坑的了!它們都是循環的!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元素恢復成數組。(其實用選擇器取的時候就是很自然的數組的形式)
這兩個實際用得太少了就不具體分析了,知道有這玩意就行吧。
The Array.from()
method creates a new Array
instance from an array-like or iterable
console.log(Array.from('foo')); // expected output: Array ["f", "o", "o"] console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6]
三.補充
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方法。
最后再啰嗦一句,歡迎大家的意見和建議,幫我糾錯,共同進步,謝謝!
附:之前學習權威指南的筆記圖
4. Much More(reduce、reduceRight、filter)
MDN:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of
1、The Array.of()
method creates a new Array
instance with a variable number of arguments, regardless of number or type of the arguments.
Array.of(7); // [7] Array.of(1, 2, 3); // [1, 2, 3] Array(7); // [ , , , , , , ] Array(1, 2, 3); // [1, 2, 3]
2、The copyWithin()
method shallow copies part of an array to another location in the same array and returns it, without modifying its size.
[1, 2, 3, 4, 5].copyWithin(-2); // [1, 2, 3, 1, 2] [1, 2, 3, 4, 5].copyWithin(0, 3); // [4, 5, 3, 4, 5] [1, 2, 3, 4, 5].copyWithin(0, 3, 4); // [4, 2, 3, 4, 5] [1, 2, 3, 4, 5].copyWithin(-2, -3, -1); // [1, 2, 3, 3, 4] [].copyWithin.call({length: 5, 3: 1}, 0, 3); // {0: 1, 3: 1, length: 5}
3、The entries()
method returns a new Array Iterator
object that contains the key/value pairs for each index in the array.
var array1 = ['a', 'b', 'c']; var iterator1 = array1.entries(); console.log(iterator1.next().value); // expected output: Array [0, "a"] console.log(iterator1.next().value); // expected output: Array [1, "b"]
4、The every()
method tests whether all elements in the array pass the test implemented by the provided function.
function isBelowThreshold(currentValue) { return currentValue < 40; } var array1 = [1, 30, 39, 29, 10, 13]; console.log(array1.every(isBelowThreshold)); // expected output: true
5、The fill()
method fills all the elements of an array from a start index to an end index with a static value.
var array1 = [1, 2, 3, 4]; // fill with 0 from position 2 until position 4 console.log(array1.fill(0, 2, 4)); // expected output: [1, 2, 0, 0] // fill with 5 from position 1 console.log(array1.fill(5, 1)); // expected output: [1, 5, 5, 5] console.log(array1.fill(6)); // expected output: [6, 6, 6, 6]
6、The filter()
method creates a new array with all elements that pass the test implemented by the provided function.
var words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = words.filter(word => word.length > 6); console.log(result); // expected output: Array ["exuberant", "destruction", "present"]
7、The find()
method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined
is returned.
var array1 = [5, 12, 8, 130, 44]; var found = array1.find(function(element) { return element > 10; }); console.log(found); // expected output: 12
8、The includes()
method determines whether an array includes a certain element, returning true
or false
as appropriate.
var array1 = [1, 2, 3]; console.log(array1.includes(2)); // expected output: true var pets = ['cat', 'dog', 'bat']; console.log(pets.includes('cat')); // expected output: true console.log(pets.includes('at')); // expected output: false
9、The keys()
method returns a new Array Iterator
object that contains the keys for each index in the array.
var arr = ['a', 'b', 'c']; var iterator = arr.keys(); console.log(iterator.next()); // { value: 0, done: false } console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: undefined, done: true }
10、The reduce()
method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.
const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // expected output: 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // expected output: 15
11、The reduceRight()
method applies a function against an accumulator and each value of the array (from right-to-left) to reduce it to a single value.
const array1 = [[0, 1], [2, 3], [4, 5]].reduceRight( (previousValue, currentValue) => previousValue.concat(currentValue) ); console.log(array1); // expected output: Array [4, 5, 2, 3, 0, 1]
12、The some()
method tests whether at least one element in the array passes the test implemented by the provided function.
function isBiggerThan10(element, index, array) { return element > 10; } [2, 5, 8, 1, 4].some(isBiggerThan10); // false [12, 5, 8, 1, 4].some(isBiggerThan10); // true