前端面试之手写代码篇(持续更新中)

136 阅读3分钟

[toc]

手写代码

数组扁平化

//demo1
 function flatten(arr) {
    let result = [];

    for (let i = 0; i < arr.length; i++) {
      if (Array.isArray(arr[i])) {
        result = result.concat(flatten(arr[i]));
      } else {
        result = result.concat(arr[i]);
      }
    }

    return result;
  }
//demo2
  const flattenArray = (arr) => Array.isArray(arr) ? arr.reduce((a, b) => [...a, ...flattenArray(b)], []) : [arr]
//demo3
function flatten(arr) {
    if(Array.isArray(arr)){
        arr.toString().split(",").map(function(item){
            return Number(item)
        })
    }
  
}

防抖和节流

防抖(debounce)

所谓防抖,就是指触发事件后 n 秒后才执行函数, 如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

举个例子:我连续输入100个字母,在输入最后一个字母后,再等200毫秒,执行请求接口。

  • 防抖的含义就是让某个时间期限内(约定200毫秒),事情处理函数只执行一次。
// 防抖
  function debounce(func,wait){
    let timeout = null //借助闭包
    return function() {
      if(timeout) clearTimeout(timeout) 
      timeout = setTimeout(() => {
        func();
      }, wait); // 简化写法
    }
  }

节流(throttle)

所谓节流,就是指连续触发事件但是在 n秒中只执行一次函数。

举个例子:我连续输入100个字母,在输入过程中,每隔300毫秒就请求一次接口。也就是说等输入完100个字母,会请求很多次接口。

  • 节流的含义就是在某个时间期限,每隔(预定300毫秒)执行一次。
//节流
function throttle(func,wait){
    let canRun = true;
    return function(){
      if(!canRun) return false;
      canRun = false;
      setTimeout(() => {
        func();
        canRun = true;
      }, wait);
    }
  }

总结

如果事件触发是高频但是有停顿时,可以选择debounce; 在事件连续不断高频触发时,只能选择throttle,因为debounce可能会导致动作只被执行一次,界面出现跳跃。

深拷贝和浅拷贝

前提

  • 防止赋值之后互相影响
  • 引用值存储在堆里,且存储的是一个指针
  • 该指针指向内存中的某个位置,该位置存储变量的实际值
var a = 2;
var a_copy = a;
a_copy = 3;
console.log("a="+a);//2
console.log("a_copy="+a_copy);//3

var b = [1,2,3];
var b_copy = b;
b_copy.push(5);
console.log("b="+b);//1,2,3,5
console.log("b_copy="+b_copy);//1,2,3,5

浅拷贝

就是只复制对象的第一层,如果对象有嵌套,内部不会复制。第一层不相等,第二层都相等

var obj = {
    a: 1,
    b: 2,
    c: 3
  }
//浅拷贝
function shallowClone(obj){
    let cloneObj = {};
    for(let i in obj){
      cloneObj[i] = obj[i];
    }
    return cloneObj;
}
var obj2 = shallowClone(obj);
console.log(obj)//
console.log(obj2)//
console.log(obj === obj2)//false
console.log(obj.c === obj2.c);//true

深拷贝

拷贝的时候会生成一份新的数据,修改拷贝以后的数据不会改变原数据。

function deepCopy(obj, cache = new WeakMap()) {
    if (!obj instanceof Object) return obj
    if (cache.get(obj)) return cache.get(obj)         // 防止循环引用
    if (obj instanceof Function) {                    // 支持函数
      return function () {
        obj.apply(this, arguments)
      }
    }
    if (obj instanceof Date) return new Date(obj)     // 支持日期
    if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)  
    // 支持正则对象
    // 还可以增加其他对象,比如:Map, Set等,根据情况判断增加即可,面试点到为止就可以了

    const res = Array.isArray(obj) ? [] : {}    // 数组是 key 为数字索引的特殊对象
    cache.set(obj, res)                         // 缓存 copy 的对象,用于处理循环引用的情况

    Object.keys(obj).forEach((key) => {
      if (obj[key] instanceof Object) {
        res[key] = deepCopy(obj[key], cache)
      } else {
        res[key] = obj[key]
      }
    });
    return res
}

柯里化

所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。

// 柯里化之前
function add(x, y) {
  return x + y;
}

add(1, 2) // 3

// 柯里化之后
function addX(y) {
  return function (x) {
    return x + y;
  };
}

addX(2)(1) // 3

进一步抽象

function currying (fn, ...args1) {
  return function (...args2) {
   return fn(...args1, ...args2)
  }
}
var increment = currying(add, 1)
increment(2) === 3
// true
var addTen = currying(add, 10)
addTen(2) === 12

手写柯里化:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数

function add(){
    var args = Array.prototype.slice.call(arguments);
    let inner = function(){
        args.push(...arguments);
        return inner;
    }
    inner.toString = function(){
        return args.reduce((p,n)=>p+n)
    }
    return inner;
}
//测试求和
add(1,2,3,4)(5,6)(7)  || add(1)(2)(3)(4)



有效大括号(leetcode 20)

  var isValid = function(str){

    var map = {
      "{":"}",
      "[":"]",
      "(":")"
    }
    if(str.length % 2 === 1) return false;
    var res = [];
    for(var i = 0;i < str.length;i++){
      if(map[str[i]]){
        res.push(str[i]);
      } else {
        var a = res[res.length-1];
        if(map[a] == str[i]) {
          res.pop()
        } else {
          return false;
        } 
      }
    }
    return res.length===0;
  }

检索url参数

 function query(){
  let url = window.location.search.split("?")[1];
  let params = url.split("&");
  const param = {};
  params.forEach(
    (i) => {
      let item = i.split("=");
      param[item[0]] = item[1];
    }
  );
  console.log(param);
  return param;
}

发布订阅模式