每日学习--数组遍历、排序、深浅拷贝1

63 阅读6分钟

数组遍历

1.map

  1. 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成

  2. 返回值:一个新数组,由callfn回调返回值组成

  3. 参数:const new arr = array.map((item)=>{return item...})

const arrs = [1, 0, 3, 21, 6, 4, 61]
//newArr由fn的返回值组成,这里必须return 否则newArr都是undefined
const newArr = arrs.map(arr => { return arr += 1})
//返回一个新数组,不会影响原数组
console.log(arrs) //1,0,3,21,6,4,61
console.log(newArr)//2,1,4,22,7,5,62
  1. 使用场景:对返回的数据不符合规范需要二次处理。

2.forEach

  1. 遍历一个数组,使数组的每项执行fn,没有返回值,fn中对item的操作不会改变原数组
  2. 返回值:undefined
  3. const new arr = array.forEach((item)=>{item...})
     /*
       关于为什么不会改变原数组的解释
       forEach在执行的过程中会创建一个临时变量,指向arr--item=arr[0]...
       这里更改item只是改变了item的指向,不会对原数组造成改变
       如果数组是引用类型,通过person.name可以更改,但是直接person=***不可以改变原数组
       */
       const arrs = [1, 0, 3, 21, 6, 4, 61]
       const newarr = arrs.forEach((item)=>{item +=1})
       console.log(arrs)//1, 0, 3, 21, 6, 4, 61 这里不会改变原数组
       console.log(newarr)//undefined
       
  1. 使用场景:做数据的列表展示(不需要对原数组数据更改)
  2. map和forEach的区别:map能实现的forEach也可以但是尽量用map,功能的分离

3.filter

  1. .filter return一个通过fn的新数组 必须取返回值,不会影响原数组

    const arrs = [1, 0, 3, 21, 6, 4, 61]
    const filterArr = arrs.filter(item => item > 3)
    console.log(filterArr); // 21 6 4 61
    

4.for of 遍历一个可迭代的数组

    for (item of arrs) {
     console.log(filterArr);//1, 0, 3, 21, 6, 4, 61
       } 

5.for in 遍历一个可枚举的对象

    for (const key in object) {
         if (Object.hasOwnProperty.call(object, key)) {
             const element = object[key];

         }
     } 
     

常见数组排序的方法

1.冒泡排序

  1. 相邻元素两两比较,较小的交换位置往前,第一趟后比较最大的放在数组最后
  2. 第一个for循环控制比较轮次,经过n-1就可以完全排序好
  3. 第二份for循环控制比较元素,每一趟比较的元素都要少一个(末尾已经找到最大的排序好)
 

       function bubbleSort(arr) {
          if (!Array.isArray(arr)) { return; }
          //控制比较轮次,一共 n-1 趟
          for (let i = 0, len = arr.length; i < len - 1; i++) {
              //控制两个挨着的元素进行比较
              for (let j = 0; j < len - 1 - i; j++) {
                  if (arr[j] > arr[j + 1]) {
                      // let tem = arr[i]
                      // arr[i] = arr[minIndex]
                      // arr[minIndex] = tem
                      [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
                  }
              }
          }
          return arr

      }
        const arrs = [5,9,7,3,1]
        console.log(bubbleSort(arrs));//1,3,5,7,9

2.选择排序

  1. 每趟比较选择数组最小的元素,记录位置,与a[i]元素交换位置
  2. 第一个for循环是选择趟数,最多经过n-1趟就可以排序好
  3. 第二个for循环比较选择最小的元素,记录位置
 const arrs = [1, 0, 3, 21, 6, 4, 61]
      function selectSort(arr) {
          if (!Array.isArray(arr)) { return; }
          //选择趟数n-1趟
          for (let i = 0, len = arr.length; i < len - 1; i++) {
              //这里以数组第一个元素作为最小值参照,记录位置
              let minIndex = i, tem;
              //比较的元素,如果比arr[minIndex]小,记录位置
              for (let j = i + 1; j < len; j++) {
                  if (arr[j] < arr[minIndex]) {
                      //如果比较值比arr[minIndex],记录下当前位置
                      minIndex = j
                  }
              }
              //交换位置
              if (minIndex !== i) {
                  tem = arr[i]
                  arr[i] = arr[minIndex]
                  arr[minIndex] = tem
              }
          }
          return arr

      }
      console.log(selectSort(arrs));

3.快速排序

快速排序的特点就是快,而且效率高!它是处理大数据最快的排序算法之一。

思想

  • 先找到一个基准点(一般指数组的中部),然后数组被该基准点分为两部分,依次与该基准点数据比较,如果比它小,放左边;反之,放右边。
  • 左右分别用一个空数组去存储比较后的数据。
  • 最后递归执行上述操作,直到数组长度 <= 1;

特点:快速,常用。

缺点:需要另外声明两个数组,浪费了内存空间资源。

            function quickSort(arr) {
                if (arr.length <= 1) return arr 
                const left = [], right = [], current = arr.splice(0, 1)
                for (let i = 0; i < arr.length; i++) {
                    if (arr[i] < current) {
                        //小于参考值放左边
                        left.push(arr[i])
                    } else {
                        //否则放右边
                        right.push(arr[i])
                    }

                }
                //递归数组,一直分到数组<=1,排序完成
                return quickSort(left).concat(current, quickSort(right))

            }
            console.log(quickSort([1,3,5,9,6]))

深浅拷贝

问题的提出 由array的concat方法返回一个对原数组的浅拷贝产生的思考?数组赋值是浅拷贝吗?

1. 赋值 =

  • 基本类型赋值,系统会为新的变量在栈内存中分配一个新值(可以看作深拷贝,两个数据不会影响)
        let a = 3
        let b = a
        a = 4
        console.log(a,b); // 4 3
    
  • 引用数据类型赋值,为新变量分配一个源对象的引用(指向源对象的地址,两个数据会相互影响)
       const obj = {name:'jack',age:18}
       //newObj和obj指向了同一个对象的引用
       const newObj = obj
       newObj.name = 'red'
       console.log(obj,newObj);//{name: 'red', age: 18} {name: 'red', age: 18}

2. 浅拷贝

什么是浅拷贝?如果是对象类型({name:'jack'}||[1,2,3]),则只拷贝一层,如果对象的属性又是一个对象,那么此时拷贝的就是此属性的引用。

对象里是基本数据类型,浅拷贝后新旧数据不影响。如果对象里是引用数据类型只是拷贝了地址,新旧数据会影响。

常见的浅拷贝(只拷贝一层)

  • Object.assign
  • Array.prototype.slice(), Array.prototype.concat()
  • [...array] 扩展运算符
      function shadowClone(obj) {
            //判断数据类型是数组还是对象
            const newObj = Array.isArray(obj) ? [] : {}
            for (const prop in obj) {
                console.log(prop);
                //过滤掉原型的属性
                if (Object.hasOwnProperty.call(obj, prop)) {
                    newObj[prop] = obj[prop];
                }
            }shenkaob
            return newObj
        }
        const obj = { name: 'jack', age: 18 }
        const newPerson = shadowClone(obj)
        newPerson.name = 'red'
        console.log(obj.name,newPerson.name); //jack red
        //浅拷贝只是拷贝了一层数据(基本数据类型),如果是对象中是引用数据类型的
        //还是拷贝的对象的引用
       //[{name:'jack',age:18}]--对象中是引用数据类型浅拷贝后新旧数据还是会影响

3. 深拷贝

深拷贝就是对对象中所有数据类型的拷贝

深拷贝就是增加一个指针,并申请一个新的内存,并且让这个新增加的指针指向这个新的内存地址使用深拷贝,在释放内存的时候就不会像浅拷贝一样出现释放同一段内存的错误,当我们需要复制原对象但有不能修改原对象的时候。

深拷贝的实现

  • JSON.parse(JSON.stringify())
  • 函数库lodash(第三方库)
  • 手写递归函数

JSON.parse(JSON.stringify(obj)) (最简单的实现深拷贝)

问题:

  1. 循环引用就报错
  2. 无法拷贝特殊的对象,比如:RegExp, Date, Set, Map等在序列化的时候会丢失。
  3. 无法拷贝函数

手写递归深拷贝

        function deepClone(obj) {
            //判断数据类型是数组还是对象,基本数据类型直接返回cloneObj(cloneObj = obj)
            let cloneObj = null
            if (typeof obj == "object" && obj !== null) {
                cloneObj = Array.isArray(obj) ? [] : {}
                for (const prop in obj) {
                    console.log(prop);
                    //过滤掉原型的属性
                    if (Object.hasOwnProperty.call(obj, prop)) {
                        cloneObj[prop] = deepClone(obj[prop]);
                    }
                }
            } else cloneObj = obj;

            return cloneObj
        }
        
        const per = [{ name: 'jack', age: 18 },{ name: 'red', age: 199}]
        const newPer = deepClone(per)
        newPer[0].age = 188
        console.log(per[0].age,[0].age);//18 188