在學習vue移動端音樂項目時,看到一個打亂數組函數,感覺很有意思就記錄一下(意外發現:slice是個有趣的知識點)
原理:遍歷數組,(let i = 0; i < _arr.length; i++),從0-i之間隨機取一個數,與當前的arr[i]作交換,這樣就把數組洗的很亂
// 返回min和max之間的一個隨機數,包括min和max
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min) // +1是保證可以取到上限值
}
// 洗牌函數
function shuffle(arr) {
let _arr = arr.slice() // 下面會講到slice的特別之處
for (let i = 0; i < _arr.length; i++) {
let j = getRandomInt(0, i)
let t = _arr[i]
_arr[i] = _arr[j]
_arr[j] = t
}
return _arr
}
var arr = [0,1,2,3,4];
shuffle(arr);
打亂數組就是這么簡單,下面讓我們說說為什么要用slice處理一下,而不是直接用arr本身???
我們在打亂數組后,一定希望的是返回一個新的打亂過的數組,原來的數組不發生變化。
因為數組是引用類型,所以不能直接操作原數組(arr)。我們需要拷貝一份,將拷貝的數組(_arr)打亂之后return出去。
那么問題來了,slice能做到操作拷貝后的數組(_arr)而不改變原數組(arr)嗎?
答案是:能做到一半
因為let _arr = arr.slice()
能將原數組(arr)的第一層深拷貝。
舉個栗子:
let obj = [
{
name:0,
job:0
},
{
name:1,
job:1
},
{
name:2,
job:2
}
];
let copy = obj.slice(0); // 0可省略
copy[1].name = 10; // 改變第二層,對象的屬性name
console.log(obj[1].name); // 10
console.log(copy[1].name); // 10
copy[2] = {name: 20,job: 20}; // 改變第一層,將整個對象替換成別的對象(改變copy[2]對象的內存指向)
console.log(obj[2].name); // 2
console.log(copy[2].name); // 20
綜上,就很好理解為什么要用let _arr = arr.slice()
處理了。
注:es6的Object.assign
、解構賦值
;Array的concat、map、filter
也只會深拷貝對象(數組)的第一層。
附加一個更騷的打亂方式:
var numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411];
var numbers_ = numbers.slice();
numbers_ = numbers_.sort(function(){ return Math.random() - 0.5});