前端常见的手写题(JS)

call函数封装

function call (Fn, obj, ...args) {
  if (obj === undefined || obj === null) {
    obj = globalThis  // 等于全局对象
  }
  //为obj创建临时方法
  obj.temp = Fn
  // 调用temp方法
  let result = obj.temp(...args)
  //删除temp方法
  delete obj.temp
  return result
}

apply函数封装

function apply (Fn, obj, args) {
  obj.temp = Fn
  let res = obj.temp(...args)
  delete obj.temp
  return res
}

bind函数封装

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

节流函数

function throttle (callback, time) {
  // 设置开始时间
  let start = 0
  // 返回结果是一个函数
  return function (e) {
    let now = Date.now()
    if (now - start >= time) {
      // 执行回调
      callback.call(this, e)
      // 修改开始时间
      start = now
    }
  }
}

防抖函数

// 当操作停止时才执行回调
function debounce (callback, time) {
  let timeId = null
  return function (e) {
    if (time !== null) {
      clearTimeout(timeId)
    }
    timeId = setTimeout(() => {
      callback.call(this, e)
      timeId = null
    }, time)
  }
}

// 执行第一次回调,当停止操作time秒后再操作的话执行
function debounce2 (callback, time) {
  let timeId = null

  return function (e) {
    let first = !timeId
    if (time !== null) {
      clearTimeout(timeId)
    }
    if (first) {
      callback.call(this, e)
    }
    timeId = setTimeout(() => {
      timeId = null
    }, time)
  }
}

map函数封装

function map (arr, callback) {
  let res = []
  for (let i = 0; i < arr.length; i++) {
    res.push(callback(arr[i], i))
  }
  return res
}

reduce函数封装

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

filter函数封装

function filter (arr, callback) {
  let result = []
  for (let i = 0; i < arr.length; i++) {
    // 循环判断回调是否为真,如果为真压入result中
    let res = callback(arr[i], i)
    if (res) {
      result.push(arr[i])
    }
  }
  return result
}

find函数封装

function find (arr, callback) {
  for (let i = 0; i < arr.length; i++) {
    let res = callback(arr[i], i)
    if (res) {
      // 如果满足,直接返回当前数
      return arr[i]
    }
  }
  // 没有找到返回undefined
  return undefined
}

findIndex函数封装

function findIndex (arr, callback) {
  for (let i = 0; i < arr.length; i++) {
    let res = callback(arr[i])
    if (res) {
      return i
    }
  }
  return undefined
}

every函数封装

function every (arr, callback) {
  for (let i = 0; i < arr.length; i++) {
    let res = callback(arr[i], i)
    if (!res) return false
  }
  return true
}

concat数组合并

function concat (arr, ...args) {
  const res = [...arr]
  args.forEach(item => {
    if (Array.isArray(item)) {
      // 如果是数组
      res.push(...item)
    } else {
      res.push(item)
    }
  })
  return res

}

slice数组切片封装

function slice (arr, start, end) {
  let res = []
  if (arr.length == 0) return []
  // 判断start
  start = start || 0
  if (start >= arr.length) {
    return []
  }
  // 判断end
  end = end || arr.length
  if (end < start) {
    end = arr.length
  }
  for (let i = start; i < end; i++) {
    res.push(arr[i])
  }
  return res
}

数组分块

// 数组分块 例如[1,2,3,4,5,6,7,8,9] 输入4分为[[1,2,3,4],[5,6,7,8],[9]]
function chunk (arr, size) {
  const res = []
  let temp = []
  arr.forEach(item => {
    if (temp.length === 0) {
      // 如果temp的长度为0,将temp临时数组压入res中
      res.push(temp)
    }
    // 先压入空数组,再往空数组中压入数据
    temp.push(item)
    //当temp长度满了,重新置为空
    if (temp.length === size) {
      temp = []
    }
  })
  return res
}

数组差集

// A数组与B数组的差集:A数组里有但是B数组没有的数
function difference (arr1, arr2 = []) {
  if (arr1.length === 0) return []  // 如果arr1数组为空直接返回空数组
  if (arr2.length === 0) return arr1.slice()  // 如果arr2为空,则返回arr1数组,但是不能返回原数组
  let res = arr1.filter(item => !arr2.includes(item))
  return res
}

创建对象

function newInstance (Fn, ...args) {
  //1,创建新对象
  let obj = {}
  //2.将新对象的__proto__指向构造函数Fn的prototype
  obj.__proto__ = Fn.prototype
  //3.执行构造函数并改变this指向
  let res = Fn.call(obj, ...args)
  //4.返回新对象,判断返回的是基本数据类型还是引用数据类型
  return res instanceof Object ? res : obj
}

浅拷贝

function clone (target) {
  // 类型判断  只有是引用数据类型才拷贝
  if (typeof target === 'object' && target !== 'null') {
    if (Array.isArray(target)) {
      // 如果是数组
      return [...target]
    } else {
      return { ...target }
    }
  } else {
    // 基本数据类型
    return target
  }
}

// es5中浅拷贝
function clone2 (target) {
  if (typeof target === 'object' && target !== null) {
    // 创建一个容器
    let res = Array.isArray(target) ? [] : {}
    for (let key in target) {
      //判断当前对象是否包含该属性,因为hasOwnProperty会把原型上的也遍历出来
      if (target.hasOwnProperty(key)) {
        res[key] = target[key]
      }

    }
    return res
  } else {
    return target
  }
}

深拷贝

// JSON实现深拷贝此方法不能克隆function,也不能循环引用
function deepClone (target) {
  let res = JSON.stringify(target)
  let data = JSON.parse(res)
  return data
}
// 使用递归实现深拷贝,可以克隆方法,但是也不能循环引用
function deepClone2 (target) {
  if (typeof target == 'object' && target !== null) {
    let res = Array.isArray(target) ? [] : {}
    for (let key in target) {
      // 判断是不是target本身的属性(for in循环会遍历原型上的属性)
      if (target.hasOwnProperty(key)) {
        res[key] = deepClone2(target[key])
      }
    }
    return res
  } else {
    return target
  }
}

检测字符串是否为回文

// 第一种最简单,使用split转为数组,再使用reverse反转后看相等不
function palindrome2 (str) {
return str.split('').reverse().join('')=== str
  
}

// 第二种 选取中间指针(长度比特右移),看左右两边是否相等
function palindrome (str) {
  let arr = str.split('')
  for (let i = 0; i < str.length >> 1; i++) {
    if (arr[i] !== arr[str.length - 1 - i]) {
      return false
    }
  }
  return true
}

事件委托函数封装

function addEventListener (el, type, Fn, selector) {
  // 判断el的类型
  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
      console.log(e)
      // 判断选择器与目标元素是否相符合
      if (target.matches(selector)) {
        // 若符合则执行回调
        Fn.call(target, e)
      }
    })
  }

}

手写事件总线EVent Bus

let eventBus = {
  // 保存类型的容器和回调
  callbacks: {}
}

eventBus.on = function (type, callback) {
  //判断callback存不存在该类型事件
  if (this.callbacks[type]) {
    this.callbacks[type].push(callback)
  } else {
    // 不存在,直接存进去,要存成数组,因为可能有多个方法
    this.callbacks[type] = [callback]
  }
}

eventBus.emit = function (type, data) {
  if (this.callbacks[type] && this.callbacks[type].length > 0) {
    this.callbacks[type].forEach(callback => {
      callback(data)
    })
  }

}

eventBus.off = function (eventName) {
  if (eventName) {
    // 若传入了eventName则只删除对应的事件类型
    delete this.callbacks[eventName]
  } else {
    // 若不传入对应的事件名,则删除所有
    this.callbacks = {}
  }
}

手写消息发布与订阅

let Pubsub = {
  // 订阅唯一id,后边可根据id取消订阅
  id: 1,
  callbacks: {
    // pay:{
    //   token1:fn,
    //   token2:fn2
    // }
  }
}

//订阅频道
Pubsub.subscribe = function (channel, callback) {
  let token = 'token' + this.id++
  if (this.callbacks[channel]) {
    // 如果callbacks存在pay属性,则直接压入回调
    this.callbacks[channel][token] = callback
  } else {
    // 如果没有,则初始化一下结构
    this.callbacks[channel] = {
      [token]: callback  //因为是一个变量所以需要中括号
    }
  }
  return token
}

//发布消息
Pubsub.publish = function (channel, data) {
  // 获取当前频道所有的订阅消息
  if (this.callbacks[channel]) {
    Object.values(this.callbacks[channel]).forEach(callback => {
      // 执行所有回调
      callback(data)
    })
  }
}
// 取消订阅
// 1.如果传入flag为空,则取消所有订阅
// 2.传入tokenid字符串
// 3.传入频道字符串
Pubsub.unSubscribe = function (flag) {
  if (flag === undefined) {
    this.callbacks = {}
  } else if (typeof flag === 'string') {
    // 判断传入的是token还是还是频道名称
    if (flag.indexOf('token') === 0) {
      let callbackObj = Object.values(this.callbacks).find(obj => obj.hasOwnProperty(flag))
      if (callbackObj) {
        delete callbackObj[flag]
      }
    } else {
      delete this.callbacks[flag]
    }
  }
}

axios函数封装

function axios ({ url, method, params, data }) {
  // 方法转化为大写
  method = method.toUpperCase()
  return new Promise((resolve, reject) => {
    //ajax四步骤
    //1.创建对象
    const xhr = new XMLHttpRequest()

    //2.初始化
    // 处理params对象为a=100&b=200
    let str = ''
    for (let k in params) {
      str += `${k}=${params[k]}&`
    }
    str = str.slice(0, -1)
    if (method == 'GET') {
      // 如果是get请求,在url后面拼接?和params
      xhr.open(method, url + '?' + str)
    } else {
      xhr.open(method, url)
    }

    //3.发送  判断是不是get请求,如果不是的话要设置content-type类型,设置请求体
    if (method == 'POST' || method == 'PUT' || method == 'DELETE' || method == 'PATCH') {
      xhr.setRequestHeader('Content-type', 'application/json')
      xhr.send(JSON.stringify(data))
    } else {
      xhr.send()
    }
    // 设置响应结果类型为json数据格式
    xhr.responseType = 'json'

    //4.处理请求响应状态码
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        //判断响应状态码 2xx
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve({
            statue: xhr.status,
            message: xhr.statusText,
            result: xhr.response
          })
        } else {
          reject(new Error('请求失败,失败的状态码为' + xhr.status))
        }
      }
    }
  })
}

axios.get = function (url, option) {
  let config = Object.assign(option, { method: 'get', url })
  console.log(config)
  return axios(config)
}
axios.post = function (url, option) {
  let config = Object.assign(option, { method: 'post', url })
  console.log(config)
  return axios(config)
}
axios.put = function (url, option) {
  let config = Object.assign(option, { method: 'put', url })
  return axios(config)
}
axios.delete = function (url, option) {
  let config = Object.assign(option, { method: 'delete', url })
  return axios(config)
}

手写检测数据类型

function getType (data) {
  let obj = {}
  let types = 'Number String Bollean Undefined Null BigInt Symbol Array Object Function Date Error RegExp'
  types.split(' ').forEach(item => {
    obj[`[object ${item}]`] = item.toLowerCase()
  })
  let res = typeof data == 'object' ? obj[Object.prototype.toString.call(data)] : typeof data
  console.log(res)
}

getType('123')
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值