关于javascript:js数组常用技巧

js数组罕用技巧

伪数组转换为真数组

数组的slice办法能够将相似数组的对象(比方document.getElementsByName('li'))变成真正的数组。
个别是须要应用数组的办法时做下转换。

var arr = Array.prototype.slice.call(arrayLike);
var arr = [].slice.call( arrayLike);

也能够应用es6Array.from

var arr = Array.from(arrayLike);

所以,Array.from其实能够这样简略实现:

Array.from = function(arr){
  return Array.prototype.slice.call(arr);
}

但事实上没有这么简略,比方Set类型的数据就无奈这样转换。它须要更简单的解决。

获取数组最初的元素

大多数人的做法:

var arr = [123, 456, 789];
var len = arr.length;
var end = arr[len-1]
console.log('end:', end)  // 'end:' 789

优化办法:

var array = [1, 2, 3, 4, 5, 6];
console.log( array.slice(-1) ); // [6]
console.log( array.slice(-1)[0] ); // 6
console.log( array.slice(-2) ); // [5,6]
console.log( array.slice(-3) ); // [4,5,6]

能够封装一个函数:

Array.prototype.last = function(){
  return this.slice(-1)[0];
};

当数组数量越大,性能差别越大。以下是一个测试方法

let arr1 = Array.from(new Array(100000000), (x, index)=>{
  return Math.random();
});

//耗时7.923ms
console.time('a');
console.log(arr1[arr1.length-1]);
console.timeEnd('a');

//耗时0.075ms
console.time('b');
console.log(arr1.slice(-1)[0]);
console.timeEnd('b');

截断数组

比方,当数组中有 10 个元素,而你只想获取其中前 5 个的话,你能够截断数组,通过设置 array.length = 5 使其更小。

var array = [1, 2, 3, 4, 5, 6];
console.log( array.length ); // 6
array.length = 3;
console.log( array.length ); // 3
console.log( array ); // [1,2,3]

合并数组

个别合并两个数组的话,通常会应用 Array.concat()

var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
console.log(array1.concat(array2)); // [1,2,3,4,5,6];

然而,这个函数并不适用于合并大的数组,因为它须要创立一个新的数组,而这会耗费很多内存。

这时,你能够应用 Array.prototype.push.apply( arr1, arr2 ) 来代替创立新的数组,它能够把第二个数组合并到第一个中,从而较少内存耗费:

var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
Array.prototype.push.apply(array1, array2);
console.log(array1);  // [1,2,3,4,5,6];

对于es6,能够应用扩大符:

var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
array1.push(...array2);
console.log(array1);  // [1,2,3,4,5,6];

数组的随机排序

办法一:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
function randSort1(arr){
    for(var i = 0,len = arr.length;i < len; i++ ){
        var rand = parseInt(Math.random()*len);
        var temp = arr[rand];
        arr[rand] = arr[i];
        arr[i] = temp;
    }
    return arr;
}
console.log(randSort1(arr));

办法二:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
function randSort2(arr){
    var mixedArray = [];
    while(arr.length > 0){
        var randomIndex = parseInt(Math.random()*arr.length);
        mixedArray.push(arr[randomIndex]);
        arr.splice(randomIndex, 1);
    }
    return mixedArray;
}
console.log(randSort2(arr));

办法三:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
  return Math.random() - 0.5;
});
console.log(arr);

数组扁平化

实现一个flatten的函数,实现拍平一个多维数组一维。示例如下:

var testArr1 = [[0, 1], [2, 3], [4, 5]];
var testArr2 = [0, [1, [2, [3, [4, [5]]]]]];
flatten(testArr1) // [0, 1, 2, 3, 4, 5]
flatten(testArr2) // [0, 1, 2, 3, 4, 5]

解法1

先来一个最简略的解法:

flatten = (arr) => arr.toString().split(',').map((val) => parseInt(val));

它是利用数组toString的特殊性,很奇妙地解决。

解法2

再来一种:

flatten2 = function (arr) {
  return arr.reduce(function (pre, val) {
    if (!Array.isArray(val)) {
      pre.push(val);
    } else {
      pre.push(...flatten2(val));
    }
    return pre;
  }, []);
};

也能简写为:

flatten2 = (arr) => arr.reduce((pre, val) => pre.concat(Array.isArray(val) ? flatten2(val) : val), []);

解法3

当初es6已有内置的flat办法:

var testArr1 = [[0, 1], [2, 3], [4, 5]];
testArr1.flat() // [0, 1, 2, 3, 4, 5]

var testArr2 = [0, [1, [2, [3, [4, [5]]]]]];
testArr2.flat() // [0, 1, [2, [3, [4, [5]]]]]

咱们看到,testArr2只解放了一层。为什么呢?

原来这个函数有个默认参数depth,咱们能够称之为深度,默认是1,当不传递参数时,只解决一层。
要想全副铺展的话,须要传递参数Infinity,如testArr1.flat(Infinity)

上面是我的一个简略实现。

Array.prototype.flat = function (depth = 1) {
    var arr = [];
    for (var i = 0; i < this.length; i++) {
        if (Array.isArray(this[i]) && depth > 0) {
            arr.push(...this[i].flat(depth-1));
        } else {
            arr.push(this[i]);
        }
    }
    return arr;
};

includes与indexOf的区别

数组有2个判断是否蕴含某个元素的办法,includesindexOf,后者很早就有了,前者是es6才加的。

很多人认为2个的区别只是前者返回bool,后者返回索引数字,其实不止如此,要害一点在于对NaN的判断。

indexOf是不会判断NaN的。比方:

var arr = [1, 2, 3, NaN];
console.log(arr.includes(NaN)); // true
console.log(arr.indexOf(NaN)); // -1

所以,咱们要实现includes的时候,须要这样:

Array.prototype.includes = function (item) {
    for (var i = 0; i < this.length; i++) {
        // if (this[i] === item || (this[i] !== this[i] && item !== item)) {
        if (this[i] === item || (isNaN(this[i]) && isNaN(item))) {
            return true;
        }
    }
    return false;
};

其中,我正文掉的那行,就是isNaN的一个实现,它是js中惟一一个不等于本身的。

数组循环办法的实现

数组的少数办法都能用原生的for循环实现,而数组的循环遍历的办法,简直都一样,都有2个参数,第一个是callback函数,第二个是扭转后面callback作用域的。
通过对这些函数的实现,能够更好地了解和记忆怎么应用。

forEach

以最简略的forEach为例,能够这么实现:

Array.prototype.forEach = function (callback, scope) {
    for (var i = 0; i < this.length; i++) {
        callback.call(scope, this[i], i, this);
    }
}

map

mapforEach的区别只是有返回值罢了:

Array.prototype.map = function (callback, scope) {
    var arr = [];
    for (var i = 0; i < this.length; i++) {
        arr.push(callback.call(scope, this[i], i, this));
    }
    return arr;
};

some

some是满足条件时,就返回true,没有一个满足时返回false

Array.prototype.some = function (callback, scope) {
    for (var i = 0; i < this.length; i++) {
        if(callback.call(scope, this[i], i, this)){
          return true;
        }
    }
    return false;
};

every

everysome刚好相同,有一个不满足条件时,就返回false,全副满足才返回true

Array.prototype.every = function (callback, scope) {
    for (var i = 0; i < this.length; i++) {
        if(!callback.call(scope, this[i], i, this)){
          return false;
        }
    }
    return true;
};

find

find是找到符合条件的元素:

Array.prototype.find = function (callback, scope) {
    for (var i = 0; i < this.length; i++) {
        if(callback.call(scope, this[i], i, this)){
          return this[i];
        }
    }
};

findIndex

findIndex是找到符合条件的元素的索引:

Array.prototype.findIndex = function (callback, scope) {
    for (var i = 0; i < this.length; i++) {
        if(callback.call(scope, this[i], i, this)){
          return i;
        }
    }
    return -1;
};

reduce的用法

数组的reduce办法比拟非凡,它的参数不像其它循环遍历的办法,有点另类。
用法是这样的:

var arr = [1, 2, 3, 4];
var callbackfn = function (pre, cur, index, arr) {
    console.log(pre, cur, index);
    return pre + cur;
};
var res = arr.reduce(callbackfn);
console.log(res); // 10

var res2 = arr.reduce(callbackfn, 5);
console.log(res2); // 15

它的回调函数里,第1个参数是上一次callback的执行后果。
第一次时,如果没有传递第2个参数,这时它应用数组的第一个元素,也将从第二元素开始遍历。
它会将最初一个执行后果返回。

罕用应用场景是做累加。

还有这样一种状况,先过滤一次条件,再用map返回一个新的数组。

var res3 = arr.filter(item => item > 2).map(i => i * 2);
console.log(res3);

这样有个弊病,会进行2次循环。最优是应用原生for进行解决,当然也能够应用reduce

var res4 = arr.reduce(function (pre, cur) {
     if (cur > 2) {
         pre.push(cur * 2);
     }
    return pre;
}, []);
console.log(res4);

咱们能够这样实现:

Array.prototype.reduce = function (callbackfn, preItem) {
    var i = 0;
    if (preItem === undefined) { //如果没有预传值,则代表默认为第一个元素,从第2个元素开始遍历
        preItem = this[0];
        i++;
    }
    for (var len = this.length; i < len; i++) {
        preItem = callbackfn(preItem, this[i], i, this);
    }
    return preItem;
};

join

join是合并数组为字符串。

Array.prototype.join = function (sep = ',') {
    var str = '';
    for (var i = 0; i < this.length; i++) {
        str += this[i] + (i === this.length - 1 ? '' : sep);
    }
    return str;
};

多层for循环时怎么跳出外围循环

作为面试题问面试者,竟然鲜有人能答复。可能当初这种应用场景渐少的缘故。

在一个函数里,return关键字天然能够跳出循环,但要持续上面的逻辑的话,就只有应用break

对于多层循环嵌套,只须要在须要中断的循环里面加个标签(轻易一个字符串都行),就能够持续用break关键字来中断循环。

a: for(var i = 0; i < 10; i++){
  for(var j = 10; j < 100; j++){
    if(j == 10){
      break a;
    }
  }
}

更多更新内容会在这里

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理