自定义函数工具库

目录

一、函数相关

1、call()& apply()& bind()

1.1、自定义函数对象的call方法

1.2、自定义函数对象的apply方法

1.3、自定义函数对象的bind方法

2、函数节流与函数防抖

2.1. 相关理解

2.2.API说明

2.3.编码实现 

二、数组相关

1、API列表

2、数组声明式系列方法

2.1.使用数组声明式系列方法

2.2.编码实现

2.3.测试

3、数组去重

3.1.API 说明

3.2. 实现

3.3. 编码实现

 4、数组合并与切片

4.1. API 说明

4.2.编码实现

       4.3 测试

5、数组扁平化

5.1 API 说明

5.2 编码实现

5.3 测试

6.数组分块

6.1.API 说明

6.2.编码实现

6.3.测试

7.数组取差异

7.1.API 说明

7.2.编码实现

7.3.测试

8.删除数组中部分元素

8.1.API相关

8.2. 编码实现 

8.3.测试

9. 得到数组的部分元素

9.1.API 相关

9.2. 编码实现

9.3.测试 

 三、对象相关 

1.相关API

2.自定义new

2.1.API 相关

2.2.编码实现

2.3.测试

3.自定义instanceof

3.1. API 相关

3.2.编码实现

3.3. 测试

4.合并多个对象

4.1.API 相关

4.2.编码实现

 5.2.实现浅拷贝

4.3.测试

5. 对象/数组拷贝

5.1.区别浅拷贝与深拷贝

5.2.实现浅拷贝

5.3.实现深拷贝

4.2. 编码实现

5.4.测试

四、字符串相关

4.1. API相关

4.2 编码实现

4.3.测试 

五、手写ajax请求函数

5.1.API 相关

5.2. 实现整体流程

5.3. 编码实现

5.4.测试



一、函数相关

1、call()& apply()& bind()

Api说明:

  • call()
    • 语法: call(fn, obj, ...args)
    • 功能: 执行fn, 使this为obj, 并将后面的n个参数传给fn(功能等同于函数对象的call方法)
  • apply()
    • 语法: apply(fn, obj, args)
    • 功能: 执行fn, 使this为obj, 并将args数组中的元素传给fn(功能等同于函数对象的apply方法)
  • bind()
    • 语法: bind(fn, obj, ...args)
    • 功能: 给fn绑定this为obj, 并指定参数为后面的n个参数 (功能等同于函数对象的bind方法)

        实现说明

  • 区别call()/apply()/bind()
    • call(obj)/apply(obj): 调用函数, 指定函数中的this为第一个参数的值
    • bind(obj): 返回一个新的函数, 新函数内部会调用原来的函数, 且this为bind()指定的第一参数的值
    • 注意: 如果obj是null/undefined, this为window
  • 应用
    • call()/apply()应用: 根据伪数组生成真数组
    • bind(): react中组件的自定义方法 / vue中的事件回调函数内部
  • 自定义call()/apply()
    • 给obj添加一个临时方法, 方法名任意, 值为当前函数
    • 通过obj调用这个临时方法, 并将接收的参数传入
    • 删除obj上的这个临时方法属性
  • 自定义实现bind()
    • 返回一个新函数
    • 在新函数内部通过原函数对象的call方法来执行原函数
    • 指定原函数的this为obj
    • 指定参数为bind调用的参数和后面新函数调用的参数
1.1、自定义函数对象的call方法

call.js 

/*
自定义函数对象的call方法
Fn:要执行的函数
obj:函数运行时this指向的对象
args:函数运行时的参数
*/
function call(Fn, obj, ...args) {
    // 如果obj是undefined/null, this指定为window
    if (obj === undefined || obj === null) {
        // return fn(...args)
        obj = window
    }
    // 给obj添加一个临时方法, 方法指向的函数就是fn
    obj.tempFn = Fn;//tempFn内部在执行时this是指向obj的,变相实现了this指向obj这样一个效果
    // 通过obj来调用这个方法 ==> 也就会执行fn函数 ==> 此时fn中的this肯定为obj
    const result = obj.tempFn(...args);
    // 删除obj上的临时方法
    delete obj.tempFn;
    // 返回fn执行的结果
    return result;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="css/style.css" rel="stylesheet">
    <script src="./call.js"></script>
</head>

<body>

    <script>
        // 声明一个函数
        function add(a, b) {
            return a + b + this.c;
        }
        // 声明一个对象
        let obj = {
            c: 521
        }
        //添加全局属性
        window.c = 1314;
        // 执行call函数
        console.log(call(add, obj, 10, 20));// 151
         console.log(call(add, null, 30, 40));// 1384
    </script>
</body>

</html>
1.2、自定义函数对象的apply方法
/*
自定义函数对象的Apply方法
Fn:要执行的函数
obj:函数运行时this指向的对象
args:函数运行时的参数
*/
// 改变this的指向,执行函数,返回结果
function apply(Fn, obj, ...args) {
    if (obj === undefined || obj === null) {
        obj = window;
    }
    //给obj添加一个临时的方法,方法指向的函数就是Fn
    obj.tempFn = Fn;
    // 通过obj来调用这个方法 ==> 也就会执行fn函数 ==> 此时fn中的this肯定为obj
    const result = obj.tempFn(...args)
    // 删除obj上的临时方法
    delete obj.tempFn
    // 返回fn执行的结果
    return resultv
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="css/style.css" rel="stylesheet">
    <script src="./call.js"></script>
</head>

<body>

    <script>
        // 声明一个函数
        function add(a, b) {
            return a + b + this.c;
        }
        // 声明一个对象
        let obj = {
            c: 521
        }
        //添加全局属性
        window.c = 1314;
        // 执行call函数
        console.log(apply(add, obj, 10, 20));// 151
        console.log(apply(add, null, 30, 40));// 1384
    </script>
</body>

</html>
1.3、自定义函数对象的bind方法
import {call} from './call'
/* 
  自定义函数对象的bind方法
*/
export function bind(fn, obj, ...args) {
  console.log('bind()')
  // 返回一个新函数
  return (... args2) => {
    // 通过call调用原函数, 并指定this为obj, 实参为args与args2
    return call(fn, obj, ...args, ...args2)
  }
}

2、函数节流与函数防抖

2.1. 相关理解

   事件频繁触发可能造成的问题?

  • 一些浏览器事件:window.onresize、window.mousemove等,触发的频率非常高,会造成界面卡顿
  • 如果向后台发送请求,频繁触发,对服务器造成不必要的压力
  • 如何限制事件处理函数频繁调用:

    • 函数节流
    • 函数防抖

函数节流(throttle)

  • 理解:
    • 在函数需要频繁触发时: 函数执行一次后,只有大于设定的执行周期后才会执行第二次
    • 适合多次事件按时间做平均分配触发
  • 场景:
    • 窗口调整(resize)
    • 页面滚动(scroll)
    • DOM 元素的拖拽功能实现(mousemove)
    • 抢购疯狂点击(click)

函数防抖(debounce)

  • 理解:
    • 在函数需要频繁触发时: 在规定时间内,只让最后一次生效,前面的不生效。
    • 适合多次事件一次响应的情况
  • 场景:
    • 输入框实时搜索联想(keyup/input)
  • 区别函数节流与防抖

2.2.API说明
  • throttle() 节流
    • 语法: throttle(callback, wait)
    • 功能: 创建一个节流函数,在 wait 毫秒内最多执行 callback 一次
  • debounce() 防抖
    • 语法: debounce(callback, wait)
    • 功能: 创建一个防抖动函数,该函数会从上一次被调用后,延迟 wait 毫秒后调用 callback
2.3.编码实现 

throttle.js: 函数节流

/* 
实现函数节流
- 语法: throttle(callback, wait)
- 功能: 创建一个节流函数,在 wait 毫秒内最多执行 `callback` 一次
*/
export function throttle(callback, wait) {
  let start = 0
  // 返回一个事件监听函数(也就是节流函数)
  return function (event) {
    console.log('throttle event')
    // 只有当距离上次处理的时间间隔超过了wait时, 才执行处理事件的函数
    const current = Date.now()
    if ( current - start > wait) {
      callback.call(this, event) // 需要指定this和参数
      start = current
    }
  }
}

 debounce.js: 函数防抖

/* 
实现函数防抖
- 语法: debounce(callback, wait)
- 功能: 创建一个防抖动函数,该函数会从上一次被调用后,延迟 `wait` 毫秒后调用 `callback`
*/
export function debounce (callback, wait) {
  // 用来保存定时器任务的标识id
  let timeoutId = -1 
  // 返回一个事件监听函数(也就是防抖函数)
  return function (event) {
    console.log('debounce event')
    // 清除未执行的定时器任务
    if (timeoutId!==-1) {
      clearTimeout(timeoutId)
    }
    // 启动延迟 await 时间后执行的定时器任务
    timeoutId = setTimeout(() => {
      // 调用 callback 处理事件
      callback.call(this, event)
      // 处理完后重置标识
      timeoutId = -1
    }, wait)
  }
}

应用

<!DOCTYPE html>
<html lang="en">

<head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="css/style.css" rel="stylesheet">
    <script src="./utils.js"></script>
</head>

<body>
    <button id="handle">正常处理</button>
    <button id="throttle">测试函数节流</button>
    <button id="debounce">测试函数防抖</button>


    <script>

        /* 处理点击事件的回调函数 */
        function handleClick(event) { // 处理事件的回调
            console.log('处理点击事件', this, event)
        }

        document.getElementById('handle').onclick = handleClick
        document.getElementById('throttle').onclick = throttle(handleClick, 2000)
        document.getElementById('debounce').onclick = debounce(handleClick, 2000)
    </script>
</body>

</html>

二、数组相关

1、API列表

  1. map()
  2. reduce()
  3. filter()
  4. find()
  5. findIndex()
  6. every()
  7. some()
  8. unique1() / unique2() / unique3()
  9. concat()
  10. slice()
  11. flatten()
  12. chunk() / chunk2()
  13. difference()
  14. pull()
  15. pullAll()
  16. drop()
  17. dropRight()

2、数组声明式系列方法

2.1.使用数组声明式系列方法
  • map(): 返回一个由回调函数的返回值组成的新数组
  • reduce(): 从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值
  • filter(): 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回
  • find(): 找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined
  • findIndex(): 找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1
  • every(): 如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。
  • some(): 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。
2.2.编码实现
  • declares.js: 实现数组声明式处理系列工具函数
/* 
实现map()
*/
export function map (array, callback) {
  const arr = []
  for (let index = 0; index < array.length; index++) {
    // 将callback的执行结果添加到结果数组中
    arr.push(callback(array[index], index))
  }
  return arr
}

/*
实现reduce() 
*/
export function reduce (array, callback, initValue) {
  let result = initValue
  for (let index = 0; index < array.length; index++) {
    // 调用回调函数将返回的结果赋值给result
    result = callback(result, array[index], index)
  }
  return result
}

/* 
实现filter()
*/
export function filter(array, callback) {

  const arr = []
  for (let index = 0; index < array.length; index++) {
    if (callback(array[index], index)) {
      arr.push(array[index])
    }
  }
  return arr
}

/* 
实现find()
*/
export function find (array, callback) {
  for (let index = 0; index < array.length; index++) {
    if (callback(array[index], index)) {
      return array[index]
    }
  }
  return undefined
}

/* 
实现findIndex()
*/
export function findIndex (array, callback) {
  for (let index = 0; index < array.length; index++) {
    if (callback(array[index], index)) {
      return index
    }
  }
  return -1
}

 /* 
 实现every()
 */
 export function every (array, callback) {
  for (let index = 0; index < array.length; index++) {
    if (!callback(array[index], index)) { // 只有一个结果为false, 直接返回false
      return false
    }
  }
  return true
}

/* 
实现some()
*/
export function some (array, callback) {
  for (let index = 0; index < array.length; index++) {
    if (callback(array[index], index)) { // 只有一个结果为true, 直接返回true
      return true
    }
  }
  return false
}
2.3.测试

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>数组声明式系列方法</title>
</head>
<body>
  <script src="../dist/atguigu-utils.js"></script>
  <script>
    /* 
    需求:
    1. 产生一个每个元素都比原来大10的新数组
    2. 得到所有奇数的和
    3. 得到值大于8且下标是偶数位的元素组成的数组
    4. 找出一个值大于8且下标是偶数位的元素
    5. 找出一个值大于8且下标是偶数位的元素的下标
    6. 判断下标为偶数的元素是否都为奇数
    7. 判断是否有下标为偶数的元素值为奇数
    */

    const arr = [1, 3, 6, 9, 15, 19, 16]

    /* 使用数组内置方法 */
    // console.log(arr.map((item, index) => item + 10))
    // console.log(arr.reduce((preTotal, item, index) => {
    //   return preTotal + (item%2===1 ? item : 0)
    // }, 0))
    // console.log(arr.filter((item, index) => item>8 && index%2===0))
    // console.log(arr.find((item, index) => item>8 && index%2===0))
    // console.log(arr.findIndex((item, index) => item>8 && index%2===0))
    // console.log(arr.every((item, index) => index%2===1 || item%2===1))
    // console.log(arr.some((item, index) => index%2===0 && item%2===1))


    /* 使用自定义工具函数 */
    console.log(aUtils.map(arr, (item, index) => item + 10))
    console.log(aUtils.reduce(arr, (preTotal, item, index) => {
      return preTotal + (item%2===1 ? item : 0)
    }, 0))
    console.log(aUtils.filter(arr, (item, index) => item>8 && index%2===0))
    console.log(aUtils.find(arr, (item, index) => item>8 && index%2===0))
    console.log(aUtils.findIndex(arr, (item, index) => item>8 && index%2===0))
    console.log(aUtils.every(arr, (item, index) => index%2===1 || item%2===1))
    console.log(aUtils.some(arr, (item, index) => index%2===0 && item%2===1))
  </script>
</body>
</html>

3、数组去重

3.1.API 说明
  • 根据当前数组产生一个去除重复元素后的新数组
  • 如: [2, 3, 2, 7, 6, 7] ==> [2, 3, 7, 6]
3.2. 实现
  • 方法1: 利用forEach()和indexOf()

    • 说明: 本质是双重遍历, 效率差些
  • 方法2: 利用forEach() + 对象容器

    • 说明: 只需一重遍历, 效率高些
  • 方法3: 利用ES6语法: from + Set 或者 ... + Set

    • 说明: 编码简洁

3.3. 编码实现
  • unique.js
/*
方法1: 利用forEach()和indexOf()
  说明: 本质是双重遍历, 效率差些
*/
export function unique1 (array) {
  const arr = []
  array.forEach(item => {
    if (arr.indexOf(item)===-1) {
      arr.push(item)
    }
  })
  return arr
}

/*
方法2: 利用forEach() + 对象容器
  说明: 只需一重遍历, 效率高些
*/
export function unique2 (array) {
  const arr = []
  const obj = {}
  array.forEach(item => {
    if (!obj.hasOwnProperty(item)) {
      obj[item] = true
      arr.push(item)
    }
  })
  return arr
}

/*
方法3: 利用ES6语法
    1). from + Set
    2). ... + Set
    说明: 编码简洁
*/
export function unique3 (array) {
  // return Array.from(new Set(array))
  return [...new Set(array)]
}

 4、数组合并与切片

4.1. API 说明
  • concat(): 合并

    • 语法: var new_array = concat(array, value1[, value2[, ...[, valueN]]])
    • 功能: 将n个数组或值与当前数组合并生成一个新数组, 原始数组不会被改变
  • slice(): 切片

    • 语法: var new_array = slice(array, [begin[, end]])
    • 功能: 返回一个由 begin 和 end 决定的原数组的浅拷贝, 原始数组不会被改变
4.2.编码实现
  • concat.js: 自定义数组合并
/* 
语法: var new_array = concat(old_array, value1[, value2[, ...[, valueN]]]) 
功能: 将n个数组或值与当前数组合并生成一个新数组
*/
export function concat (array, ...values) {
  const arr = [...array]
  values.forEach(value => {
    if (Array.isArray(value)) {
      arr.push(...value)
    } else {
      arr.push(value)
    }
  })
  return arr
}
  • src/array/slice.js: 自定义数组切片
    /* 
      语法: var new_array = slice(oldArr, [begin[, end]])
      功能: 返回一个由 begin 和 end 决定的原数组的浅拷贝, 原始数组不会被改变
    */
    export function slice (array, begin, end) {
      // 如果当前数组是[], 直接返回[]
      if (array.length === 0) {
        return []
      }
    
      // 如果begin超过最大下标, 直接返回[]
      begin = begin || 0
      if (begin >= array.length) {
        return []
      }
    
      // 如果end不大于begin, 直接返回[]
      end = end || array.length
      if (end > array.length) {
        end = array.length
      }
      if (end <= begin) {
        return []
      }
    
      // 取出下标在[begin, end)区间的元素, 并保存到最终的数组中
      const arr = []
      for (let index = begin; index < end; index++) {
        arr.push(array[index])
      }
    
      return arr
    }
           4.3 测试
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>数组合并与切片</title>
    </head>
    <body>
    
      <script src="../dist/atguigu-utils.js"></script>
      <script>
        console.log(aUtils.concat([1, 2], [3, 4], 6))  // [1, 2, 3, 4, 6]
    
        console.log(aUtils.slice([1, 3, 5, 7, 9]))  // [1, 3, 5, 7, 9]
        console.log(aUtils.slice([1, 3, 5, 7, 9], 1, 3)) // [3, 5]
        console.log(aUtils.slice([1, 3, 5, 7, 9], 1, 10)) // [3, 5, 7, 9]
      </script>
    </body>
    </html>

    5、数组扁平化

    5.1 API 说明
  • 语法: flatten(array)
  • 取出嵌套数组(多维)中的所有元素放到一个新数组(一维)中
  • src/array/flatten.js

  • 方法一: 递归 + reduce() + concat()

  • 方法二: ... + some() + concat()

  • 如: [1, [3, [2, 4]]] ==> [1, 3, 2, 4]

JavaScript中的数组some()方法用于检查数组中是否至少有一个元素满足指定条件。该方法会遍历数组,并对每个元素应用指定的测试函数。如果有任何一个元素使得测试函数返回true,则some()方法返回true,否则返回false。

5.2 编码实现
/* 
数组扁平化: 取出嵌套数组(多维)中的所有元素放到一个新数组(一维)中
  如: [1, [3, [2, 4]]]  ==>  [1, 3, 2, 4]
*/

/*
方法一: 递归 + reduce() + concat()
*/
export function flatten1 (array) {
  return array.reduce((pre, item) => {
    if (Array.isArray(item) && item.some(cItem => Array.isArray(cItem))) {
      return pre.concat(flatten1(item))
    } else {
      return pre.concat(item)
    }
  }, [])
}

/*
方法二: ... + some() + concat()
*/
export function flatten2 (array) {
  let arr = [].concat(...array)
  while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr
}
5.3 测试
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>数组扁平化</title>
</head>
<body>
  <script src="../dist/atguigu-utils.js"></script>
  <script>
    console.log(aUtils.flatten1([1, [3, [2, 4]]]))
    console.log(aUtils.flatten2([1, [3, [2, 4]]]))
  </script>
</body>
</html>

6.数组分块

6.1.API 说明
  • 语法: chunk(array, size)
  • 功能: 将数组拆分成多个 size 长度的区块,每个区块组成小数组,整体组成一个二维数组
  • 如: [1, 3, 5, 6, 7, 8] 调用chunk(arr, 4) ==> [[1, 3, 5, 6], [7,8]]
6.2.编码实现

/* 
将数组拆分成多个 size 长度的区块,每个区块组成小数组,整体组成一个二维数组
*/
export function chunk (array, size) {
  if (array.length===0) {
    return []
  }
  size = size || 1

  const bigArr = []
  let smallArr = []

  array.forEach(item => {
    if (smallArr.length===0) {
      bigArr.push(smallArr)
    }
    smallArr.push(item)
    if (smallArr.length===size) {
      smallArr = []
    }
  })

  return bigArr
}
6.3.测试
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>数组分块</title>
</head>
<body>
  <script src="../dist/atguigu-utils.js"></script>
  <script>
    console.log(aUtils.chunk([1, 2, 3, 4, 5, 6, 7], 3)) // [[1,2,3], [4,5,6],[7]]
    console.log(aUtils.chunk([1, 2, 3, 4, 5, 6, 7]))// [[1],[2],[3],[4],[5],[6],[7]]
    console.log(aUtils.chunk([1, 2, 3, 4, 5, 6, 7], 8))// [[1, 2, 3, 4, 5, 6, 7]]
  </script>
</body>
</html>

7.数组取差异

7.1.API 说明
  • 语法: difference(arr1, arr2)
  • 功能: 得到当前数组中所有不在arr中的元素组成的数组(不改变原数组)
  • 例子: difference([1,3,5,7], [5, 8]) ==> [1, 3, 7]
7.2.编码实现
  • src/array/difference.js
    /* 
    difference(arr1, arr2): 得到arr1中所有不在arr2中的元素组成的数组(不改变原数组)
        如: [1,3,5,7].difference([5, 8])  ==> [1, 3, 7]
    */
    export function difference (arr1, arr2) {
      if (arr1.length===0) {
        return []
      } else if (arr2.length===0) {
        return arr1.slice()
      }
      return arr1.filter(item => arr2.indexOf(item)===-1)
    }
    7.3.测试
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>数组取差异</title>
    </head>
    <body>
      <script src="../dist/atguigu-utils.js"></script>
      <script>
        console.log(aUtils.difference([1,3,5,7], [5, 8]))
      </script>
    </body>
    </html>

    8.删除数组中部分元素

    8.1.API相关
  • pull(array, ...values):
    • 删除原数组中与value相同的元素, 返回所有删除元素的数组
    • 说明: 原数组发生了改变
    • 如: pull([1,3,5,3,7], 2, 7, 3, 7) ===> 原数组变为[1, 5], 返回值为[3,3,7]
  • pullAll(array, values):
    • 功能与pull一致, 只是参数变为数组
    • 如: pullAll([1,3,5,3,7], [2, 7, 3, 7]) ===> 数组1变为[1, 5], 返回值为[3,3,7]
8.2. 编码实现 
/* 
1. pull(array, ...values): 
  删除数组中与value相同的元素, 返回所有删除元素的数组
  说明: 数组发生了改变
  如: pull([1,3,5,3,7], 2, 7, 3, 7) ===> 数组变为[1, 5], 返回值为[3,3,7]
2. pullAll(array, values): 
  功能与pull一致, 只是参数变为数组
  如: pullAll([1,3,5,3,7], [2, 7, 3, 7]) ===> 数组变为[1, 5], 返回值为[3,3,7]
*/
export function pull (array, ...values) {
  if (array.length===0 || values.length===0) {
    return []
  }
  
  var result = []
  for (let index = 0; index < array.length; index++) {
    const item = array[index]
     if (values.indexOf(item)!==-1) {
      array.splice(index, 1) //删除指定元素,index开始的位置,1表示删除一个
      result.push(item)
      index--
    }
  }

  return result
}

export function pullAll (array, values) {
  if (!values || !Array.isArray(values)) {
    return []
  }
  return pull(array, ...values)
}
 8.3.测试
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>删除数组中部分元素</title>
</head>
<body>
  <script src="../dist/atguigu-utils.js"></script>
  <script>
    var arr = [1,3,5,3,7]
    console.log(aUtils.pull(arr, 2, 7, 3, 7), arr)
    var arr2 = [1,3,5,3,7]
    console.log(aUtils.pullAll(arr2, [2, 7, 3, 7]), arr2)
  </script>
</body>
</html>

9. 得到数组的部分元素

9.1.API 相关
  • drop(array, count)
    • 得到当前数组过滤掉左边count个后剩余元素组成的数组
    • 说明: 不改变当前数组, count默认是1
    • 如: drop([1,3,5,7], 2) ===> [5, 7]
  • dropRight(array, count)
    • 得到当前数组过滤掉右边count个后剩余元素组成的数组
    • 说明: 不改变当前数组, count默认是1
    • 如: dropRight([1,3,5,7], 2) ===> [1, 3]
9.2. 编码实现
/* 
1. drop(array, count): 
   得到数组过滤掉左边count个后剩余元素组成的数组
   说明: 不改变当前数组, count默认是1
   如: drop([1,3,5,7], 2) ===> [5, 7]
2. dropRight(array, count): 
   得到数组过滤掉右边count个后剩余元素组成的数组
   说明: 不改变数组, count默认是1
   如: dropRight([1,3,5,7], 2) ===> [1, 3]
*/

export function drop (array, count=1) {
  if (array.length === 0 || count >= array.length) {
    return []
  }

  return array.filter((item, index) => index>=count)
}

export function dropRight (array, count=1) {
  if (array.length === 0 || count >= array.length) {
    return []
  }

  return array.filter((item, index) => index < array.length-count)
}
9.3.测试 
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>得到数组的部分元素</title>
</head>
<body>
  <script src="../dist/atguigu-utils.js"></script>
  <script>
    console.log(aUtils.drop([1,3,5,7], 2))
    console.log(aUtils.drop([1,3,5,7], 4))
    console.log(aUtils.drop([1,3,5,7]))

    console.log(aUtils.dropRight([1,3,5,7], 2))
    console.log(aUtils.dropRight([1,3,5,7], 4))
    console.log(aUtils.dropRight([1,3,5,7]))
  </script>
</body>
</html>

 三、对象相关 

1.相关API

  • newInstance()
  • myInstanceOf()
  • mergeObject()
  • clone1() / clone2()
  • deepClone1() / deepClone2() / deepClone3() / deepClone4()

2.自定义new

2.1.API 相关
  • 语法: newInstance(Fn, ...args)
  • 功能: 创建Fn构造函数的实例对象
2.2.编码实现
  • src/object/newInstance.js
    export function newInstance (Fn, ...args) {
      // 创建一个空的object实例对象obj, 作为Fn的实例对象
      const obj = {}
      // 将Fn的prototype属性值赋值给obj的__proto__属性值
      obj.__proto__ = Fn.prototype
      // 调用Fn, 指定this为obj, 参数为args列表
      const result = Fn.call(obj, ...args)
      // 如果Fn返回的是一个对象类型, 那返回的就不再是obj, 而是Fn返回的对象
      // 否则返回obj
      return result instanceof Object ? result : obj
    }
    2.3.测试
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>自定义new</title>
    </head>
    <body>
      <script src="../dist/atguigu-utils.js"></script>
      <script>
        function Person(name, age) {
          this.name = name
          this.age = age
          // return {}
          // return []
          // return function (){}
          // return 1
          // return undefined
        }
    
        const p = new Person('tom', 12)
        console.log(p)
    
        const p2 = aUtils.newInstance(Person, 'Jack', 13)
        console.log(p2, p2.constructor)
      </script>
    </body>
    </html>

    3.自定义instanceof

    3.1. API 相关
    语法: myInstanceOf(obj, Type) 功能: 判断obj是否是Type类型的实例

    3.2.编码实现
    src/object/myInstanceOf.js
    • 实现: Type的原型对象是否是obj的原型链上的某个对象, 如果是返回tru, 否则返回false
export function myInstanceOf(obj, Type) {
  // 得到原型对象
  let protoObj = obj.__proto__

  // 只要原型对象存在
  while(protoObj) {
    // 如果原型对象是Type的原型对象, 返回true
    if (protoObj === Type.prototype) {
      return true
    }
    // 指定原型对象的原型对象
    protoObj = protoObj.__proto__
  }
  
  return false
}
 3.3. 测试
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>自定义instanceof</title>
</head>
<body>
  <script src="../dist/atguigu-utils.js"></script>
  <script>
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    const p = new Person('tom', 12)
    
    console.log(aUtils.myInstanceOf(p, Object), p instanceof Object)
    console.log(aUtils.myInstanceOf(p, Person), p instanceof Person)
    console.log(aUtils.myInstanceOf(p, Function), p instanceof Function)
  </script>
</body>
</html>

4.合并多个对象

4.1.API 相关
  • 语法: object mergeObject(...objs)
  • 功能: 合并多个对象, 返回一个合并后对象(不改变原对象)
  • 例子:
    • { a: [{ x: 2 }, { y: 4 }], b: 1}
    • { a: { z: 3}, b: [2, 3], c: 'foo'}
    • 合并后: { a: [ { x: 2 }, { y: 4 }, { z: 3 } ], b: [ 1, 2, 3 ], c: 'foo' }
4.2.编码实现

 5.2.实现浅拷贝

  • src/object/mergeObject.js
    export function mergeObject(...objs) {
      const result = {}
    
      // 遍历objs
      objs.forEach(obj => {
        Object.keys(obj).forEach(key => {
          // 如果result还没有key值属性
          if (!result.hasOwnProperty(key)) {
            result[key] = obj[key]
          } else { // 否则 合并属性
            result[key] = [].concat(result[key], obj[key])
          }
        })
      })
    
      // 可以使用reduce来代替forEach手动添加
      return result
    }
    4.3.测试
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>合并多个对象</title>
    </head>
    <body>
      <script src="../dist/atguigu-utils.js"></script>
      <script>
        const object = {
          a: [{ x: 2 }, { y: 4 }],
          b: 1
        }
        const other = {
          a: { z: 3},
          b: [2, 3],
          c: 'foo'
        }
        console.log(aUtils.merge(object, other)) 
      </script>
    </body>
    </html>

    5. 对象/数组拷贝

    5.1.区别浅拷贝与深拷贝
  • 纯语言表达:
    • 浅拷贝: 只是复制了对象属性或数组元素本身(只是引用地址值)
    • 深拷贝: 不仅复制了对象属性或数组元素本身, 还复制了指向的对象(使用递归)
  • 举例说明: 拷贝persons数组(多个人对象的数组)
    • 浅拷贝: 只是拷贝了每个person对象的引用地址值, 每个person对象只有一份
    • 深拷贝: 每个person对象也被复制了一份新的
5.2.实现浅拷贝
  • src/object/clone.js
    /* 
    实现浅拷贝
      方法一: 利用ES6语法
      方法二: 利用ES5语法: for...in
    */
    /* 方法一: 利用ES6语法*/
    export function clone1(target) {
      // 如果是对象(不是函数, 也就是可能是object对象或者数组)
      if (target!=null && typeof target==='object') {
        if (target instanceof Array) {
          // return target.slice()
          // return target.filter(() => true)
          // return target.map(item => item)
          return [...target]
        } else {
          // return Object.assign({}, target)
          return {...target}
        } 
      }
      // 基本类型或者函数, 直接返回
      return target
    }
    
    /* 方法二: 利用ES5语法: for...in */
    export function clone2(target) {
      if (target!=null && typeof target==='object') {
        const cloneTarget = Array.isArray(target) ? [] : {}
        for (let key in target) {
          if (target.hasOwnProperty(key)) {
            cloneTarget[key] = target[key]
          }
        }
        return cloneTarget
      } else {
        return target
      }
    }
    5.3.实现深拷贝

4.2. 编码实现

  • 实现一: 大众乞丐版

    • 问题1: 函数属性会丢失
    • 问题2: 循环引用会出错
  • 实现二: 面试基础版

    • 解决问题1: 函数属性还没丢失
  • 实现三: 面试加强版本

    • 解决问题2: 循环引用正常
  • 实现四: 面试加强版本2(优化遍历性能)

    • 数组: while | for | forEach() 优于 for-in | keys()&forEach()
    • 对象: for-in 与 keys()&forEach() 差不多
  • 编码实现: src/object/deepClone.js

    /* 
    深度克隆
    1). 大众乞丐版
        问题1: 函数属性会丢失
        问题2: 循环引用会出错
    2). 面试基础版本
        解决问题1: 函数属性还没丢失
    3). 面试加强版本
        解决问题2: 循环引用正常
    4). 面试加强版本2(优化遍历性能)
        数组: while | for | forEach() 优于 for-in | keys()&forEach() 
        对象: for-in 与 keys()&forEach() 差不多
    */
    
    /* 
    1). 大众乞丐版
        问题1: 函数属性会丢失
        问题2: 循环引用会出错
    */
    export function deepClone1(target) {
      return JSON.parse(JSON.stringify(target))
    }
    
    /* 
    2). 面试基础版本
        解决问题1: 函数属性还没丢失
    */
    export function deepClone2 (target) {
      if (target!==null && typeof target==='object') {
        const cloneTarget = target instanceof Array ? [] : {}
      
        for (const key in target) {
          if (target.hasOwnProperty(key)) {
            cloneTarget[key] = deepClone2(target[key])
          }
        }
    
        return cloneTarget
      }
    
      return target
    }
    
    /* 
    3). 面试加强版本
        解决问题2: 循环引用正常
    */
    export function deepClone3 (target, map=new Map()) {
      if (target!==null && typeof target==='object') {
        // 从缓存容器中读取克隆对象
        let cloneTarget = map.get(target)
        // 如果存在, 返回前面缓存的克隆对象
        if (cloneTarget) {
          return cloneTarget
        }
        // 创建克隆对象(可能是{}或者[])  
        cloneTarget = target instanceof Array ? [] : {}
        // 缓存到map中
        map.set(target, cloneTarget)
    
        for (const key in target) {
          if (target.hasOwnProperty(key)) {
            // 递归调用, 深度克隆对象, 且传入缓存容器map
            cloneTarget[key] = deepClone3(target[key], map)
          }
        }
    
        return cloneTarget
      }
    
      return target
    }
    
    /* 
    4). 面试加强版本2(优化遍历性能)
        数组: while | for | forEach() 优于 for-in | keys()&forEach() 
        对象: for-in 与 keys()&forEach() 差不多
    */
    export function deepClone4 (target, map=new Map()) {
      if (target!==null && typeof target==='object') {
        // 从缓存容器中读取克隆对象
        let cloneTarget = map.get(target)
        // 如果存在, 返回前面缓存的克隆对象
        if (cloneTarget) {
          return cloneTarget
        }
        // 创建克隆对象(可能是{}或者[])  
        if (target instanceof Array) {
          cloneTarget = []
          // 缓存到map中
          map.set(target, cloneTarget)
          target.forEach((item, index) => {
            cloneTarget[index] = deepClone4(item, map)
          })
        } else {
          cloneTarget = {}
          // 缓存到map中
          map.set(target, cloneTarget)
          Object.keys(target).forEach(key => {
            cloneTarget[key] = deepClone4(target[key], map)
          })
        }
    
        return cloneTarget
      }
    
      return target
    }
    5.4.测试
  • 浅拷贝测试
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>浅克隆/浅复制/浅拷贝</title>
    </head>
    <body>
      <!-- 
        实现浅拷贝
            方法一: 利用ES6语法
            方法二: 利用ES5语法: for...in
      -->
      <script src="../dist/atguigu-utils.js"></script>
      <script>
        const obj1 = { x: 'abc', y: {m: 1} }
        // const obj2 = aUtils.clone1(obj1)
        const obj2 = aUtils.clone2(obj1)
        console.log(obj2, obj2===obj1, obj2.x===obj1.x, obj2.y===obj1.y)
    
        const arr1 = ['abc', {m: 1}]
        // const arr2 = aUtils.clone1(arr1)
        const arr2 = aUtils.clone2(arr1)
        console.log(arr2, arr2===arr1, arr2[0]===arr1[0], arr2[1]===arr1[1])
      </script>
    </body>
    </html>
  • 深拷贝测试
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>深度克隆/深复制/深拷贝</title>
    </head>
    <body>
      <script src="https://cdn.bootcss.com/lodash.js/4.17.15/lodash.min.js"></script>
      <script src="../dist/atguigu-utils.js"></script>
      <script>
        const obj1 = { 
          a: 1,
          b: [ 'e', 'f', 'g'],
          c: { h: { i: 2 } },
          d: function (){}
         }
         obj1.b.push(obj1.c)
         obj1.c.j = obj1.b
         
        // const obj2 = _.cloneDeep(obj1)
        // const obj2 = aUtils.deepClone1(obj1)
        // const obj2 = aUtils.deepClone2(obj1)
        // const obj2 = aUtils.deepClone3(obj1)
        const obj2 = aUtils.deepClone4(obj1)
        console.log(obj2, obj2.c === obj1.c, obj2.d===obj1.d)
      </script>
    </body>
    </html>

    四、字符串相关

    4.1. API相关
  • 字符串倒序
    • 语法: reverseString(str)
    • 功能: 生成一个倒序的字符串
  • 字符串是否是回文
    • 语法: palindrome(str)
    • 功能: 如果给定的字符串是回文,则返回 true ;否则返回 false
4.2 编码实现
  • src/string/index.js
  • 截取字符串
    • 语法: truncate(str, num)
    • 功能: 如果字符串的长度超过了num, 截取前面num长度部分, 并以...结束
/* 
1. 字符串倒序: reverseString(str)  生成一个倒序的字符串
*/
export function reverseString(str) {
	// return str.split('').reverse().join('')
    // return [...str].reverse().join('')
    return Array.from(str).reverse().join('')
}

/* 
2. 字符串是否是回文: palindrome(str) 如果给定的字符串是回文,则返回 true ;否则返回 false
*/
export function palindrome(str) {
    return str === reverseString(str)
}

/* 
3. 截取字符串: truncate(str, num) 如果字符串的长度超过了num, 截取前面num长度部分, 并以...结束
*/
export function truncate(str, num) {
	return str.length > num ? str.slice(0, num) + '...' : str
}
4.3.测试 
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>字符串处理的系列方法</title>
</head>
<body>
  <script src="../dist/atguigu-utils.js"></script>
  <script>
    console.log(aUtils.reverseString('abcd')) // dcba
    console.log(aUtils.palindrome('abcb'), aUtils.palindrome('abcba')) // false true
    console.log(aUtils.truncate('boomerang', 7)) // boomera...
  </script>
</body>
</html>

五、手写ajax请求函数

5.1.API 相关
  • 语法:
    • axios(options)
      • 参数配置对象:url, method, params与data
      • 返回值为:promise对象
    • axios.get(url, options)
    • axios.post(url, data, options)
    • axios.put(url, data, options)
    • axios.delete(url, options)
  • 功能:使用xhr发送ajax请求的工具函数,与axios库功能类似
5.2. 实现整体流程
  1. 函数的参数为一个配置对象

    { url: '', // 请求地址 method: '', // 请求方式GET/POST/PUT/DELETE params: {}, // GET/DELETE请求的query参数 data: {}, // POST或DELETE请求的请求体参数 }

  2. 返回值: 函数的返回值为promise, 成功的结果为response, 失败的结果为error

  3. 能处理多种类型的请求: GET/POST/PUT/DELETE

  4. 响应json数据自动解析为js的对象/数组

5.3. 编码实现
  • src/axios/index.js
    /* 发送任意类型请求的函数 */
    function axios({
      url,
      method='GET',
      params={},
      data={}
    }) {
      // 返回一个promise对象
      return new Promise((resolve, reject) => {
    
        // 处理method(转大写)
        method = method.toUpperCase()
        
        // 处理query参数(拼接到url上)   id=1&xxx=abc
        /* { id: 1, xxx: 'abc'} */
        let queryString = ''
        Object.keys(params).forEach(key => {
          queryString += `${key}=${params[key]}&`
        })
        if (queryString) { // id=1&xxx=abc&
          // 去除最后的&
          queryString = queryString.substring(0, queryString.length-1)
          // 接到url
          url += '?' + queryString
        }
    
    
        // 1. 执行异步ajax请求
        // 创建xhr对象
        const request = new XMLHttpRequest()
        // 打开连接(初始化请求, 没有请求)
        request.open(method, url, true)
        
        // 发送请求
        if (method==='GET') {
          request.send()
        } else if (method==='POST' || method==='PUT' || method==='DELETE'){
          // 告诉服务器请求体的格式是json
          request.setRequestHeader('Content-Type', 'application/json;charset=utf-8') 
           // 发送json格式请求体参数
          request.send(JSON.stringify(data))
        }
        
        // 绑定状态改变的监听
        request.onreadystatechange = function () {
          // 如果请求没有完成, 直接结束
          if (request.readyState!==4) {
            return
          }
          // 如果响应状态码在[200, 300)之间代表成功, 否则失败
          const {status, statusText} = request
          // 2.1. 如果请求成功了, 调用resolve()
          if (status>=200 && status<=299) {
            // 准备结果数据对象response
            const response = {
              data: JSON.parse(request.response),
              status,
              statusText
            }
            resolve(response)
          } else { // 2.2. 如果请求失败了, 调用reject()
            reject(new Error('request error status is ' + status))
          }
        }
      })
    }
    
    /* 发送特定请求的静态方法 */
    axios.get = function (url, options) {
      return axios(Object.assign(options, {url, method: 'GET'}))
    }
    axios.delete = function (url, options) {
      return axios(Object.assign(options, {url, method: 'DELETE'}))
    }
    axios.post = function (url, data, options) {
      return axios(Object.assign(options, {url, data, method: 'POST'}))
    }
    axios.put = function (url, data, options) {
      return axios(Object.assign(options, {url, data, method: 'PUT'}))
    }
    
    export default axios
    
    5.4.测试
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>手写ajax请求函数</title>
    </head>
    <body>
      <button onclick="testGet()">发送GET请求</button><br>
      <button onclick="testPost()">发送POST请求</button><br>
      <button onclick="testPut()">发送PUT请求</button><br>
      <button onclick="testDelete()">发送Delete请求</button><br>
        
    <script src="../dist/atguigu-utils.js"></script>
    <script>
      const { axios } = aUtils
    
      /* 1. GET请求: 从服务器端获取数据*/
      function testGet() {
        axios({
          // url: 'http://localhost:3000/posts',
          url: 'http://localhost:3000/posts2',
          method: 'GET',
          params: {
            id: 1,
            xxx: 'abc'
          }
        })
        // axios.get('http://localhost:3000/posts', {params: {id: 1}})
        .then(
          response => {
            console.log(response)
          },
          error => {
            alert(error.message)
          }
        )
      }
    
      /* 2. POST请求: 服务器端保存数据*/
      function testPost() {
        axios({
          url: 'http://localhost:3000/posts',
          method: 'POST',
          data: {
            "title": "json-server---", 
            "author": "typicode---"
          }
        })
        // axios.post('http://localhost:3000/posts', {title: 'aaa', author: 'bbb'})
        .then(
          response => {
            console.log(response)
          },
          error => {
            alert(error.message)
          }
        )
      }
    
      /* 3. PUT请求: 服务器端更新数据*/
      function testPut() {
        axios({
          url: 'http://localhost:3000/posts/1',
          method: 'put',
          data: {
            "title": "json-server+++", 
            "author": "typicode+++"
          }
        })
        // axios.put('http://localhost:3000/posts/1', {title: 'aaa222', author: 'bbb222'})
        .then(
          response => {
            console.log(response)
          },
          error => {
            alert(error.message)
          }
        )
      }
    
      /* 2. DELETE请求: 服务器端删除数据*/
      function testDelete() {
        axios({
          url: 'http://localhost:3000/posts/2',
          method: 'delete'
        })
        // axios.delete('http://localhost:3000/posts/2')
        .then(
          response => {
            console.log(response)
          },
          error => {
            alert(error.message)
          }
        )
      }
    </script>
    </body>
    </html>

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值