Skip to content

Latest commit

 

History

History
765 lines (671 loc) · 15.2 KB

自定义工具函数库.md

File metadata and controls

765 lines (671 loc) · 15.2 KB

来源

尚硅谷 https://www.bilibili.com/video/BV1Cy4y117vt?p=7

1.函数相关

call

function call(Fn, obj, ...args) {
  if (obj === undefined || obj === null) {
 		obj = globalThis // 全局对象
  }
  obj.temp = Fn
  let result = obj.temp(...args)
  delete obj.temp // 删除temp方法
  return result
}

apply

function apply(Fn, obj, args) {
  if (obj === undefined || obj === null) {
 		obj = globalThis // 全局对象
  }
  obj.temp = Fn
  let result = obj.temp(...args)
  delete obj.temp
  return result
}

bind

function bind(Fn, obj, ...args) {
  return function(...arg2) {
    return call(Fn, obj, ...args, ...args2)
  }
}

防抖

function debounce(fn, delay) {
  var timer = null
  return function() {
    if (timer) {
      clearTimeout(timer)
    }
    let _this = this
    let _arguments = arguments
    timer = setTimeout(function() {
      fn.apply(_this, _arguments)
    }, delay)
  }
}

节流

function throttle(fn, interval) {
  var start = 0
  return function() {
    let _this = this
    let _arguments = arguments
    let now = new Date().getTime()
    if (now - start > interval) {
      fn.apply(_this, _arguments)
      lastTime = newTime
    }
  };
}

2.数组相关

map

function map(arr, fn) {
  let result = []
  for(let i=0; i<arr.length; i++) {
    result.push(fn(arr[i], i)) // 传入的i是下标
  }
  return result
}

reduce

function reduce(arr, fn, initValue) {
  let result = initValue
  for(let i=0; i<arr.length; i++) {
    result = fn(result, arr[i])
  }
  return result
}

filter

function filter(arr, fn) {
  let result = []
  for(let i=0; i<arr.length; i++) {
    let res = fn(arr[i], i) // 传入的i是下标
    if (res) {
      result.push(arr[i])
    }
  }
  return result
}

find

function find(arr, fn) {
  for(let i=0; i<arr.length; i++) {
  	let res = fn(arr[i], i) // 传入的i是下标
    if (res) {
      return arr[i]
    }
  }
  return undefined
}

findIndex

function find(arr, fn) {
  for(let i=0; i<arr.length; i++) {
  	let res = fn(arr[i], i) // 传入的i是下标
    if (res) {
      return i
    }
  }
  return -1
}

every、some

every():如果数组中的每个元素都满足测试函数,则返回true ,否则返回false。some():如果数组中至少有一个元素满足测试函数,则返回true,否则返回false。

function every(arr, fn) {
  for(let i=0; i<arr.length; i++) {
  	if(!fn(arr[i], i)) { // 传入的i是下标
    	return false
    }
  }
  return true
}

function some() {
  for(let i=0; i<arr.length; i++) {
  	if(fn(arr[i], i)) { // 传入的i是下标
    	return true
    }
  }
  return false
}

数组去重1/2/3

// 1.forEach()和indexOf():本质是双重遍历,效率低
function unique(arr) {
  let result = []
  arr.forEach(i => {
    if(result.indexOf(i) === -1) {
      result.push(i)
    }
  })
  return result
}

// 2.forEach() + 对象容器:只需一重遍历,效率高
function unique2(arr) {
  let result = []
  let obj = {} // 用于快速判断元素是否已经存在
  arr.forEach(i => {
    if (obj[i] === undefined) {
      obj[i] = true
      result.push(i)
    }
  })
  return result
}

// 3.利用ES6语法 from + Set:编码简洁
function unique3(arr) {
  let set = new Set(arr)
  let array = [...set]
  return array
  // 简洁:return [...new Set(arr)]
}

concat数组合并

function concat(arr, ...args) {
  let result = [...arr]
  args.forEach(item => {
    if(Array.isArray(item)) {
      result.push(...item)
    } else {
      result.push(item)
    }
  })
  return result
}

slice

function slice(arr, start, end) {
  // 优化
  if (arr.length === 0) {
    return []
  }
  start = start || 0
  if (start >= arr.length) {
    return []
  }
  end = end || arr.length
  if (end < start) {
    end = arr.length
  }
  
  // 本体
  let result = []
  for(let i=0; i<arr.length; i++) {
    if (i >= start && i < end) {
      result.push(arr[i])
    }
  }
  return result
}

数组扁平化

// 将[1,2,[3,4,[5,6]],7]变为[1,2,3,4,5,6,7]

function flatten1(arr) {
  let result = []
  arr.forEach(item => {
    if(Array.isArray(item)) {
    	result.concat(flatten(item)) // 递归
    } else {
      result = result.concat(item)
    }
  })
  return result
}

function flatten2(arr) {
  let result = [...arr]
  while(result.some(item => Array.isArray(item))) {
 		result = [].concat(...result)
  }
  return result
}

数组分块

// [1,2,3,4,5,6] =chunk(arr, 4)=> [[1,2,3,4],[5,6]]

function chunk(arr, size=1) {
  let result = []
  let temp = []
  arr.forEach(item => {
    if(temp.length === 0) {
      result.push(temp)
    }
    temp.push(item)
    if(temp.length === size) {
      temp = []
    }
  })
  return result
}

数组差集

// difference([1,3,5,7],[5,8]) ==> [1,3,7]

function difference(arr1, arr2=[]) {
  // 优化
  if(arr1.length === 0) {
    return []
  }
  ir(arr2.length === 0) {
    return arr1.slice()
  }
  // 本体
  let result = arr1.filter(item => !arr2.includes(item))
  return result
}

删除数组元素

// pull([1,3,5,7],2,7,3,7) ==> 原数组变为[1,5],返回值为[3,3,7]
function pull(arr, ...args) {
  let result = [] // 保存删除的元素
  for(let i=0; i<arr.length; i++) {
    if(args.includes(arr[i])) {
      result.push(arr[i])
      arr.splice(i, 1)
      i--
    }
  }
  return result
}

// pullAll([1,3,5,3,7],[2,7,3,7]) ==> 数组变为[1,5],返回值为[3,3,7]
function pullAll(arr, values) {
  retur pull(arr, ...values)
}

获取数组某些元素

// drop(array,count) 得到当前数组过滤掉左边count个元素后的数组 drop([1,3,5,7],2) ==> [5,7]
function drop(arr, size) {
  // return arr.filter((value, index) => {
  //   return index >= size
  // })
  return arr.filter((value, index) => index>=size)
}

// dropRight(array,count) 得到当前数组过滤掉右边count个元素后的数组 drop([1,3,5,7],2) ==> [1,3]
function dropRight(arr, size) {
  // return arr.filter((value, index) => {
  //   return index < arr.length - size
  // })
  return arr.filter((value, index) => index<arr.length-size)
}

3.对象相关

创建新对象

// let obj = newInstance(Person, '张三', 18)  相当于new关键字的功能
function newInstance(Fn, ...args) {
  // 1.创建一个新对象
  let obj = {}
  // 2.修改函数内部this指向新对象 并执行
  let result = Fn.call(obj, ...args)
  // 修改新对象的原型对象
  obj.__proto__ = Fn.prototype
  // 3.返回新对象
  return result instanceof Object ? result : obj
}

比对对象类型

// 用于判断obj是否是Type类型的实例
function instanceOf(obj, Fn) {
  // 获取函数的显式原型
  let prototype = Fn.prototype
  // 获取obj的隐式原型对象
  let proto = obj.__proto__
  // 遍历原型链
  while(proto) {
  	// 检测原型对象是否相等
    if(prototype === proto) {
      return true
    }
    // 如果不等于
    proto = proto.__proto__
  }
  return false
}

对象合并

// 有重名的属性,则合并而不是覆盖
function mergeObject(...objs) {
  // 声明一个空对象
  let result = {}
  // 遍历所有的参数对象
  objs.forEach(obj => {
    // 获取当前对象所有的属性
    Object.keys(obj).forEach(key => {
      // 检测 result 中是否存在 key 属性
      if(result.hasOwnProperty(key)) {
        result[key] = [].concat(result[key], obj[key])
      } else {
        // 如果没有,则直接写入
        result[key] = obj[key]
      }
    })
  })
  return result
}

浅拷贝

function clone1(target) {
  if(typeof target === 'object' && target !== null) {
    if(Array.isArray(target) {
    	return [...target]   
    } else {
      return {...target}
    }
  } else {
    return target
  }
}

function clone2(target) {
  if(typeof target === 'object' && target !== null) {
    let result = Array.isArray(target) ? [] : {}
    for(let key in target) {
      if(target.hasOwnProperty(key)) {
        result[key] = target[key]
      }
    }
    return result
  } else {
    return target
  }	
}

深拷贝

// 问题1:函数属性会丢失
// 问题2:循环引用会出错
function deepClone1(target) {
  let str = JSON.stringfy(target)
  let data = JSON.parse(str)
  return data
}

// 解决问题1:函数属性会丢失
function deepClone2(target) {
  if(typeof target === 'object' && target !== null) {
  	let result = Array.isArray(target) ? [] : {}
    for(let key in obj) {
      // 检测该属性是否为对象本身的属性(不能拷贝原型对象的属性)
      if(target.hasOwnProperty(key)) {
        // 拷贝
      	result[key] = deepClone2(target[key]) // 递归
      }
    }
    return result
  } else {
    return target
  }
}

// 解决问题2:循环引用会出错
function deepClone3(target, map=new Map()) {
  if(typeof target === 'object' && target !== null) {
    // 克隆数据之前,进行判断,数据之前是否克隆过
    let cache = map.get(target) // map用于判断是否已经克隆过
    if(cache) {
      return cache
    }
  	let result = Array.isArray(target) ? [] : {}
    // 将新的结果存入到容器中
    map.set(target, result)
    for(let key in obj) {
      // 检测该属性是否为对象本身的属性(不能拷贝原型对象的属性)
      if(target.hasOwnProperty(key)) {
        // 拷贝
      	result[key] = deepClone3(target[key], map) // 递归
      }
    }
    return result
  } else {
    return target
  }
}

// 优化遍历性能
function deepClone4(target, map=new Map()) {
  if(typeof target === 'object' && target !== null) {
    // 克隆数据之前,进行判断,数据之前是否克隆过
    let cache = map.get(target) // map用于判断是否已经克隆过
    if(cache) {
      return cache
    }
    let isArray = Array.isArray(target)
  	let result = isArray ? [] : {}
    // 将新的结果存入到容器中
    map.set(target, result)
    if(isArray) {
      target.forEach((item, index) => {
        result[index] = deepClone4(item, map)
      })
    } else {
      // 如果是对象,获取所有的键名,再forEach遍历
      Object.keys(target).forEach(keys => {
        result[key] = deepClone4(target[key], map)
      })
    }
    return result
  } else {
    return target
  }
}

4.字符串相关

反转

function reverseString(str) {
  let arr = [...str] // 将字符串转为数组
  arr.reverse() // 反转数组
  let s = arr.join('') // 将数组拼接成字符串
  return s
}

检测是否为回文

function palindrome(str) {
  return reverseString(str) === str
}

截断

function truncate(str, length) {
  return str.slice(0, length) + '...'
}

5.手写DOM事件监听(带委托)

事件委托函数封装

function addEventListener(el, type, fn, selector) {
  if(typeof el === 'string') {
    el = document.querySelector(el)
  }
  // 事件绑定
  // 若没有传递子元素的选择器,则给 el 元素绑定事件
  if(!selector) {
    el.addEventListener(type, fn)
  } else {
    el.addEventListener(type, function(e) {
      // 获取点击的目标事件源
      let target = e.target
      // 判断选择器与目标元素是否相符合
      if(target.matches(selector)) {
        // 若符合,则调用回调
        fn.call(target, e)
      }
    })
  }
}

手写事件总线

let eventBus = {
  // 保存类型与回调的容器
  callbacks: {
    login: [fn, fn2]
  }
}

// 绑定事件
eventBus.on = function(type, callback) {
  // 回调
  if(this.callbacks[type]) {
    // 如果 callbacks 属性中存在该类型事件
    this.callbacks[type].push(callback)
  } else {
    // 如果 callbacks 属性中不存在该类型事件
    this.callbacks[type] = [callback]
  }
}

// 触发事件
event.emit = function(type, data) {
  if(this.callbacks[type] && this.callbacks[type].length) {
    // 遍历数组
    this.callbacks[type].forEach(callback => {
      callback(data)
    })
  }
}

// 解绑事件
eventBus.off = function(eventName) {
  // 若传入了eventName事件类型
  if(eventName) {
    // 只删除eventName对应的事件回调
    delete this.callbacks[eventName]
  } else {
    this.callbacks = {}
  }
}

消息订阅与发布

let PubSub = {
  id: 1, //订阅唯一id
  callbacks: {} // 频道与回调保存容器
}

// 订阅频道
PubSub.subscribe = function(channel, callback) {
  // 创建唯一的编号
  let token = "token_" + this.id++
  // pay  token_1
  if(this.callbacks[channel]) {
    this.callbacks[channel][token] = callback
  } else {
    this.callbacks[channel] = {
      [token]: callback
    }
  }
  // 返回频道订阅的id
  return token
}

// 发布消息
PubSub.publish = function(channel, data) {
	// 获取当前频道中所有的回调
  if(this.callbacks[channel]) {
Object.values(this.callbacks[channel]).forEach(callback => {
      callback(data)
    })
  }
}

// 取消订阅
/*
	1.没有传值,flag 为 undefined
	2.传入token字符串
	3.msgName字符串
*/
PubSub.publish = function(flag) {
  // 如果flag为undefined 则清空所有订阅
  if(flag === undefined) {
    this.callbacks = {}
  } else if(typeof flag === 'string') {
    // 判断是否为 token_ 开头
    if(flag.indexOf('token_') === 0) {
      // 如果是 标明是一个订阅id
let callbackObj = Object.values(this.callbacks).find(obj => obj.hasOwnProperty(flag))
      if(callbackObj) {
        delete callbackObj[flag]
      }
    } else {
      // 表明是一个频道的名称
      delete this.callbacks[flag]
    }
  }
}

6.axios

axios函数、方法封装

/* 函数封装 */
function axios({method, url, params, data}) {
  // 转换为大写
  method = method.toUpperCase()
  return new Promise((resolve, reject) => {
    // 1.创建对象
    const xhr = new XMLHttpRequest()
    // 2.初始化
    // 处理 params 对象
    let str = ''
    for(let k in params) {
      str += `${k}=${params[k]}&`
    }
    str = str.slice(0, -1)
    xhr.open(method, url+'?'+str)
    // 3.发送
    if(method === 'POST' || method === 'PUT' ||| method === 'DELETE') {
      // Content-type mime类型设置
    	xhr.setRequestHeader('Content-type', 'application/json')
      //设置请求体
      xhr.send(JSON.stringfy(data))
    } else {
      xhr.send()
    }
    // 设置响应结果的类型为 JSON
    xhr.responseType = 'json'
    // 4.处理结果
    xhr.onreadystatechange = function() {
      //
      if(xhr.readyState === 4) {
        // 判断响应状态吗
        if(xhr.status >= 200 && xhr.status < 300) {
          // 成功的状态
          resolve({
            status: xhr.status,
            message: xhr.statusText,
            body: xhr,response
          })
        } else {
          // 失败的状态
          reject(new Error('请求失败,失败的状态码为' + xhr.status))
        }
      }
    }
    xhr.send()
  })
}

/* 方法封装 */
axios.get = function(url, options) {
  // 发送 AJAX 请求 GET
  return axios(Object.assign(options, {method:'GET', url: url}))
}

axios.post = function(url, options) {
  // 发送 AJAX 请求 GET
  return axios(Object.assign(options, {method:'POST', url: url}))
}

axios.delete = function(url, options) {
  // 发送 AJAX 请求 GET
  return axios(Object.assign(options, {method:'DELETE', url: url}))
}