手写题整理

2021-02-15 面试

# 函数柯里化

function curry(fn) {
  return function sub(...args1) {
    if (fn.length <= args1.length) {
      return fn.apply(this, args1)
    } else {
      return function (...args2) {
        return sub.apply(this, args1.push(...args2))
      }
    }
  }
}

# 深浅拷贝

# 浅拷贝

  • Object.assign
Object.assign({}, obj)
  • 解构
let a = {...obj}
  • Array
let res = [1,2,3].slice(0)
let res = [].concat([1,2,3])

# 深拷贝

  • JSON
JSON.parse(JSON.stringify(target))
  • 手写【未解决循环引用等问题】
function deepClone(target) {
  let res
  // 判断 target类型
  if (getType(target) === "[object Object]") {
    res = {}
  } else if (getType(target) === "[object Array]") {
    res = []
  } else {
    return target
  }
  // 递归判断内部
  for (let i in target) {
    if (getType(target[i]) === "[object Object]"
      || getType(target[i]) === "[object Array]") {
      res[i] = deepClone(target[i])
    } else {
      res[i] = target[i]
    }
  }
  return res
}

function getType(tar) {
  return Object.prototype.toString.call(tar)
}

# 防抖与节流

# 防抖

function debounce(fn, delay) {
  let timer

  return function (...args) {
    if (timer) {
      clearTimeout(timer)
    }

    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay);
  }
}

# 节流

function throttle(fn, delay) {
  let timer = null
  let now = Date.now()
  let last = 0

  return function (...args) {
    if (now - last > delay) {// 第一次触发或者已经超过delay后再触发
      last = now
      fn.apply(this, args)
    } else if (timer === null) {// 定时、延迟运行+给null
      timer = setTimeout(() => {
        timer = null
        fn.apply(this, args)
      }, delay);
    }
  }
}

# call/bind/apply

# call

Function.prototype.myCall = function (context = globalThis) {
  if (typeof this !== 'function') throw new TypeError('Error')

  context.fn = this
  const args = Array.from(arguments).slice(1)
  const result = context.fn(...args)
  delete context.fn
  return result
};
// 实验
this.s = "out"
function pp(a, b) {
  console.log("pp" + a + b + this.s)
}
let qq = {
  s: "qqq"
}
pp("a", "b") // ppabout
pp.call(qq, "a1", "a2") // ppa1a2qqq
pp.myCall(qq, "a11", "a22") // ppa11a22qqq

# apply

Function.prototype.myApply = function (context = globalThis, arr) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  context.fn = this
  let result
  if (arr) {
    // 需要判断 arr 存在且是数组
    if (Object.prototype.toString.call(arr) !== '[object Array]') throw new TypeError("Error")
    result = context.fn(...arr)
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
};
// 实验
this.s = "out"
function pp(a, b) {
  console.log("pp" + a + b + this.s)
}
let qq = {
  s: "qqq"
}
pp("a", "b") // ppabout
pp.apply(qq, ["a1", "a2"]) // ppa1a2qqq
pp.myApply(qq, ["a11", "a22"]) // ppa11a22qqq

# bind

Function.prototype.myBind = function (context = globalThis) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  fn = this
  const args = Array.from(arguments).slice(1)

  return function F() {
    // 这里处理一下,因为arguments带有我们不需要的callee、Symbol(Symbol.iterator)等
    let fArgs = Array.from(arguments)
    // 如果是 new F 的话,this的指向就不是传入的context了
    if (this instanceof F) {
      return fn.apply(this, args.concat(fArgs))
    } else {
      return fn.apply(context, args.concat(fArgs))
    }
  }
}

// 实验
this.s = "out"
function pp(a, b) {
  console.log(...arguments,"pp" + a + b + this.s)
}
let qq = {
  s: "qqq"
}
pp.bind(qq, "a1", "a2")("arg1", "arg2") // a1 a2 arg1 arg2 ppa1a2qqq
pp.myBind(qq, "a11", "a22")("arg1", "arg2") // a11 a22 arg1 arg2 ppa11a22qqq

# Promise

# Promise.all

function myPromiseAll(arr) {
  return new Promise((resolve, reject) => {
    // 存储结果
    const res = []
    // 存储完成的 Pormise 数量
    let count = 0
    let len = arr.length
    // 循环 arr
    for (let i in arr) {
      // 赋值时如果count==len说明全部结束
      // 如果有reject直接reject
      Promise.resolve(arr[i]).then(data => {
        res[i] = data
        if (++count === len) {
          resolve(res)
        }
      }).catch(err => {
        reject(err)
      })
    }
  })
}
/*---------- 测试 ----------*/
let p1 = Promise.resolve(1),
  p2 = Promise.resolve(2),
  p3 = Promise.resolve(3)

myPromiseAll([p1, p2, p3]).then(res => {
  console.log(res) //[1, 2, 3]
}).catch(err => {
  console.log(err);
})

# Promise.race

注意不能用 .catch() ,不然的话会输出 2 ,因为微任务队列里 p1 的 catch 会排在 p2 的 then 后面

function myPromiseRace(arr) {
  return new Promise((resolve, reject) => {
    for (let item of arr) {
      Promise.resolve.then(
          res => { resolve(res) },
          err => { reject(err) }
        )
    }
  })
}
/*---------- 测试 ----------*/
let p1 = Promise.resolve(1),
  p2 = Promise.resolve(2),
  p3 = Promise.resolve(3)

myPromiseRace([p1, p2, p3]).then(res => {
  console.log(res) 
}).catch(err => {
  console.log(err);
})
输出1

# JSONP

/**
 * 
 *  最终想要的连接效果:
 * <script src='http://localhost:8080/api/jsonp?id=1&cb=jsonpCallback' type='text/javascript'></script>
 * 步骤:
 * 1. 前端和后端约定 cb 字段用来存放前端执行回调的函数名,假如是 jsonpCallback
 * 2. 后端根据params的参数获取指定内容之后,可能返回如 jsonpCallback({id:123})这样的内容
 * 3. 前端接收后,执行 window.jsonpCallback({id:123})
 */
const JSONP = ({
  url,
  params = {},
  callbackkey = 'cb', // 与后台约定的回调函数是用哪个字段
  callback // 拿到数据之后执行的回调函数
}) => {
  // 回调执行的函数
  const callBackName = "jsonpCallback"
  // cb=jsonpCallback 放入链接的参数中
  params[callbackkey] = callBackName
  // 后端返回 jsonpCallback(xxx) 之后执行内容
  window[callBackName] = callback

  const paramStr = Object.keys(params).map((key) => {
    return `${key}=${params[key]}`
  }).join('&')

  const script = document.createElement('script')
  script.setAttribute('src', `${url}?${paramStr}`)
  document.append(script)
}

# new

function mynew(func, ...args) {
  const obj = {}
  // 原型链指向修整
  obj.__proto__ = func.prototype
  // 这里要执行 func,并且以obj为this,带着参数执行
  let result = func.apply(obj, args)
  // 如果 result(也就是func执行的返回结果)非对象(可能是值类型),那么我们不能要这个结果,就返回obj这个指向func的空对象
  if (typeof result === 'object' && result !== null) {
    return result
  } else {
    return obj
  }
}

# 数组去重

# Set

[...new Set([1,1,2])]

# 手写

function quchong(arr) {
  const res = []
  for (let item of arr) {
    if (res.indexOf(item) === -1) {
      res.push(item)
    }
  }
  return res
}

# 数组扁平化

function flat(arr) {
  const res = []

  const func = (inarr) => {
    for (let item of inarr) {
      if (Array.isArray(item)) {
        func(item)
      } else {
        res.push(item)
      }
    }
  }
  func(arr)
  return res
}

flat([1, [2, [3, [4, [5]]]]])// [1, 2, 3, 4, 5]

# Ajax

const getData = (url) => {
  return new Promise((resolve, reject) => {
    // 设置 XMLHttpRequest 请求
    const xhr = new XMLHttpRequest();

    // 设置请求方法和 url
    xhr.open('GET', url);

    // 设置请求头
    xhr.setRequestHeader('Accept', 'application/json');

    // 设置请求的时候,readyState 属性变化的一个监控
    xhr.onreadystatechange = (res) => {

      // 如果请求的 readyState 不为 4,说明还没请求完毕
      if (xhr.readyState !== 4) {
        return;
      }

      // 如果请求成功(200),那么 resolve 它,否则 reject 它
      if (xhr.status === 200) {
        resolve(xhr.responseText);
      } else {
        reject(new Error(xhr.responseText));
      }
    };

    // 发送请求
    xhr.send();
  })
};

补充:Ajax 状态

  • 0 - 未初始化。尚未调用 open() 方法
  • 1 - 启动。已经调用 open() 方法,但尚未调用 send() 方法。
  • 2 - 发送。已经调用 send() 方法,但尚未接收到响应。
  • 3 - 接收。已经接收到部分响应数据。
  • 4 - 完成。已经接收到全部响应数据,而且已经可以在客户端使用了。
Powered By Valine
v1.4.14