前端面试常见手写代码

81 阅读4分钟

必背js代码

深拷贝

function deepClone(oldobj) {
  if (typeof oldobj !== "object" || oldobj == null) {
    return oldobj;
  }
  let reslut;
  if (oldobj instanceof Array) {
    reslut = [];
  } else {
    reslut = {};
  }
  for (let key in oldobj) {
    if (oldobj.hasOwnProperty(key)) {
      reslut[key] = deepClone(oldobj[key]);
    }
  }
  return reslut;
}

发布订阅

// 发布订阅模式  订阅和发布 如[fn1, fn2, fn3]
function Dep() {
    // 一个数组(存放函数的事件池)
    this.subs = [];
}
Dep.prototype = {
    addSub(sub) {   
        this.subs.push(sub);    
    },
    notify() {
        // 绑定的方法,都有一个update方法
        this.subs.forEach(sub => sub.update());
    }
};
// 监听函数
// 通过Watcher这个类创建的实例,都拥有update方法
function Watcher(fn) {
    this.fn = fn;   // 将fn放到实例上
}
Watcher.prototype.update = function() {
    this.fn();  
};

let watcher = new Watcher(() => console.log(111));  // 
let dep = new Dep();
dep.addSub(watcher);    // 将watcher放到数组中,watcher自带update方法, => [watcher]
dep.addSub(watcher);
dep.notify();   //  111, 111

实现eventbus

function Event() {
    // 存储不同的事件类型对应不同的处理函数,保证后续emmit可以执行
    this.cache = {};
}
// 订阅
Event.prototype.on = function(type, handle) {
    if(!this.cache[type]) {
        this.cache[type] = [handle];
    }else {
        this.cache[type].push(handle);
    }
}

// 发布
Event.prototype.emmit = function() {
    // 没有保证参数是多少个 就用arguments
    var type = arguments[0];
    var arg = [].slice.call(arguments, 1);
    for(var i = 0; i < this.cache[type].length; i++) {
        this.cache[type][i].apply(this, arg);
    }
}

Event.prototype.empty = function(type) {
    this.cache[type] = [];
}
Event.prototype.remove = function(type, handle) {
    this.cache[type] = this.cache[type].filter(function(ele) {
        return ele != handle;
    }) 
}
Event.prototype.once = function(type, handle) {
    if(!this.cache[type]) {
        this.cache[type] = [];
    }
    // 做标记
    handle.tag = 'true';
    this.cache[type].push(handle);
}


// 测试
function deal1(time) {
    console.log('overtime1:' + time);
}
function deal2(time) {
    console.log('overtime2:' + time);
}
var oE = new Event();
oE.on('over', deal1);
oE.on('over', deal2);
oE.emmit('over', '2018-9-25');
oE.remove('over', deal2);
oE.emmit('over', 'seconde-1-1');

数组拍平

function fn(arr) {
  let arr1 = [];
  arr.forEach((val) => {
    if (val instanceof Array) {
      arr1 = arr1.concat(fn(val));
    } else {
      arr1.push(val);
    }
  });
  return arr1;
}

节流

// 所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数
function throttle(fn, delay) {
  let timer = null;
  return function(){
    if(timer){
      return ;
    }else{
      timer = setTimeOut(() => {
        fn();
        timer = null;
      },delay)
    }
  }
}

防抖

//所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
function debounce(fn, delay){
  let timer = null;
  return function(){
    if(timer){
      clearTimeout(timer)
    }else{
      timer = setTimeOut(() => {
        fn();
      },delay)
    }
  }
}

instanceof实现

function myinstanceof(left, right){
  if(! typeof left == 'object' || left == null){
    return false;
  }

  while(true){
    if(left === null){
      return false;
    }else{
      left.__proto__ = right.prototype;
      return true;
      left = left.__proto__;
    }
  }
}

手写Promise

class Mypromise {
  static PENDING = "待定";
  static FULFILLE = "成功";
  static REJECTED = "失败";
  constructor() {
    this.status = Mypromise.PENDING;
    this.reslut = null;
    this.resloveCallbacks = [];
    this.rejectCallbacks = [];
  }
  reslove(result){
    if (this.status === MyPromise.PENDING) {
        this.status = MyPromise.FULFILLE;
        this.result = result;
        this.resloveCallbacks.forEach((callback) => {
          callback(result);
        });
      }
  }
  reject(result){
    if (this.status === MyPromise.PENDING) {
        this.status = MyPromise.REJECTED;
        this.result = result;
        this.rejectCallbacks.forEach((callback) => {
          callback(result);
        });
      }
  }
  then(onFULFILLED, onREJECTED){
    return new MyPromise((resolve, reject) => {
      onFULFILLED = typeof onFULFILLE === "function" ? onFULFILLED : () => {};
      onREJECTED = typeof onFULFILLE === "function" ? onREJECTED : () => {};
      if (this.status === MyPromise.PENDING) {
        this.rejectCallbacks.push(onREJECTED);
        this.resloveCallbacks.push(onFULFILLED);
      }
      if (this.status === MyPromise.FULFILLE) {
        setTimeout(() => {
          onFULFILLED(this.result);
        });
      }
      if (this.status === MyPromise.REJECTED) {
        setTimeout(() => {
          onREJECTED(this.result);
        });
      }
    });
  }
}

手写promise.all

function promiseAll(promiseArray) {
  return new Promise((resolve,reject) => {
    if(!Array.isArray(promiseArray)){
      return reject(new Error("错误"));
    }
    const len = promiseArray.length;
    let res = [];
    let counter = 0;
    for(let i = 0; i < len; i ++){
      Promise().resolve(promiseArray[i])
      .then(value => {
        counter ++;
        res[i] = value;
        if(counter === len){
          reslove(res);
        }
      })
      .catch(e => {
        reject(e);
      }
    }
  })
}

手写new

function myNew(fn,...arg) {
  let obj = {};
  obj.__proto__ = fn.prototype;
  let res = fn.apply(this,arg);
  return res instanceof Object ? res : obj;
}

简单实现响应式函数,对一个对象内的所有的key添加响应式特性?

// 简单实现响应式函数,对一个对象内的所有的key添加响应式特性?
// 要求最终的输出如下方代码所示

const render = (key, val) => {
  console.log(`set key = ${key} vale = ${val}`);
};

const defineReactive = (obj, key, val) => {
  if (typeof obj === "object") {
    for (const key in obj) {
      defineReactive(obj, key, obj[key]);
    }
  }
  Object.defineProperty(obj, key, {
    get() {
      return val;
    },
    set(newVale) {
      if (val === newVale) {
        return;
      }
      val = newVale;
      render(key, val);
    },
  });
};

const data = {
  a: 1,
  b: 2,
  c: {
    c1: {
      af: 999,
    },
    c2: 4,
  },
};

reactive(data);

data.a = 5; // set key = a; val = 5;
data.b = 7; // set key = b; val = 7;
data.c.c2 = 4;
data.c.c1.af = 121; // set key = af; val = 121;

括号匹配

// 括号匹配算法
function isValid(str){
    let strArr = str.split(''),
        left = [];// 空栈
    for(let i=0;i<strArr.length;i++){
        if(strArr[i] == '(' || strArr[i] == '[' || strArr[i] == '{'){
            left.push(strArr[i]) //左括号入栈
        }else{
            if(strArr[i] == ')' && left.pop() != '('){
                return false //结束循环
            }
            if(strArr[i] == ']' && left.pop() != '['){
                return false 
            }
            if(strArr[i] == '}' && left.pop() != '{'){
                return false
            }
        }
    }
    return left.length == 0
}

let test = '{9()32358}'
console.log(isValid(test)) 

请简单实现双向数据绑定mvvm

<input type="text" id="input">


const data = {};
const input = document.getElementById("input");
Object.defineProperty(data, "text", {
    set(value) {
        input.value = value;
        this.value = value;
    }
})
input.onchange = function(e) {
    data.text = e.target.value;
}

虚拟dom转真实dom

// 虚拟DOM转真实DOM
function _render(vNode) {
  //如果是数字转为字符串
  if (typeof vNode === "number") {
    vNode = String(vNode);
  }
  // 字符串转文本节点
  if (typeof vNode === "string") {
    return document.createTextNode(vNode);
  }

  //普通的DOM
  const dom = document.createElement(vNode.tag);
  if (vNode.attrs) {
    Object.keys(vNode.attrs).forEach((key) => {
      const value = vNode.attrs[key];
      dom.setAttribute(key, value);
    });
  }
  //递归
  vNode.children.forEach((child) => {
    dom.appendChildren(_render(child));
    return dom;
  });
}

遍历DOM节点

function traversalDom(element, callback) {
  callback(element);
  element = element.firstElementChild;
  while (element) {
    traversalDom(element, callback);
    element = element.nextElementSibling;
  }
}
const subTree = document.getElementById("content");
const callback = (subTree) => {
  console.log(
    element.innerText,
    element.tagName,
    "子元素的个数:",
    element.childElementCount
  );
};
traversalDom(subTree, callback);

简单轮询

function setTimer () {
        let timer;
        axios.post(url, params)
        .then(function (res) {
            if(res){
                console.log(res);
                timer = setTimeout(() => {
                    this.setTimer()
                }, 5000)       
            }else {
                clearTimeout(timer) //清理定时任务
            }

        })
        .catch(function (error) {
                console.log(error);
        });
},

手写Promise.finally

Promise.finally = function (callback) {
      return this.then(data => {
        return Promise.resolve(callback()).then(value => {
          return value;
        })
      }, err => {
        return Promise.reject(callback()).then(err => {
          throw err;
        })
      })
    }

手写Promise.race

Promise.race = function (promises) {
      return new Promise((resolve, rejected) => {
        // 将参数转化为数组对象
        promises = Array.from(promises);
        if (promises.length == 0)
          return resolve([]);
        for (let i = 0; i < promises.length; i++) {
          // 解决参数不是promise的情况
          Promise.resolve(promises[i]).then(data => {
            resolve(data);
            return;
          }, err => {
            reject(err);
            return;
          })
        }
      })
    }

冒泡排序

function bubbleSort(arr) {
let len = arr.length;
for(let i = 0; i < len; i ++){
    for(let j = 0; j < len - 1 - i; j ++)
        if(arr[j] > arr[j + 1]){
            let temp = arr[j + 1];
            arr[j + 1] = arr[j];
            arr[j] = temp;
        }
}
return arr;
}

快速排序

def quick_sort(data):    
    """快速排序"""    
    if len(data) >= 2:  # 递归入口及出口        
        mid = data[len(data)//2]  # 选取基准值,也可以选取第一个或最后一个元素        
        left, right = [], []  # 定义基准值左右两侧的列表        
        data.remove(mid)  # 从原始数组中移除基准值        
        for num in data:            
            if num >= mid:                
                right.append(num)            
            else:                
                left.append(num)        
        return quick_sort(left) + [mid] + quick_sort(right)    
    else:        
        return data

选择排序

 function selectionSort(arr) {
 var len = arr.length;
 var minIndex, temp;
 for (var i = 0; i < len - 1; i++) {
    minIndex = i;
    for (var j = i + 1; j < len; j++) {
        if (arr[j] < arr[minIndex]) {     // 寻找最小的数
            minIndex = j;                 // 将最小数的索引保存
        }
    }
    temp = arr[i];
    arr[i] = arr[minIndex];
    arr[minIndex] = temp;
 }
 return arr;
 }

插入排序

 function insertionSort(arr) {
 var len = arr.length;
 var preIndex, current;
 for (var i = 1; i < len; i++) {
    preIndex = i - 1;
    current = arr[i];
    while(preIndex >= 0 && arr[preIndex] > current) {
        arr[preIndex+1] = arr[preIndex];
        preIndex--;
    }
    arr[preIndex+1] = current;
 }
  return arr;
 }

二叉树层次遍历

var levelOrder = function (root) {
  if (!root) return [];
  let res = [];
  let queue = [];
  queue.push(root);
  while (queue.length) {
    let level = [];
    let size = queue.length;
    for (let i = 0; i < size; i++) {
      let node = queue.shift();
      level.push(node.val);
      if (node.left) {
        queue.push(node.left);
      }
      if (node.right) {
        queue.push(node.right);
      }
    }
    res.push(level);
  }
  return res;
};

二叉树非递归遍历

function vist(p) {
  console.log(p.data);
}
// 中
var inorderTraversal = function(root) {
  if (!root) return [];
  const stack = [];
  let cur = root;
  const res = [];
  while (stack.length || cur) {
    // 左节点都先压入栈
    while (cur) {
      stack.push(cur);
      cur = cur.left;
    }
    const node = stack.pop();
    res.push(node.val);
    if (node.right != null) {
      cur = node.right;
    }
  }
  return res;
};
// 前
function preOrder(T) {
  let p = T;
  let stack = [];
  while (p || stack.length) {
    if (p) {
      vist(p);
      stack.push(p.data);
      p = p.left;
    } else {
      p = stack.pop();
      p = p.right;
    }
  }
}
// 后
function postOrder(T) {
    let p = T;
    let stack = [];
    let r = null;
    while (p || stack.length) {
      if (p) {
        stack.push(p.data);
        p = p.left;
      } else {
        let node = stack.slice(-1);
        if(!node.right && !node.right == r){
            p = p.right;
        }else{
            p = stack.pop();
            vist(p);
            r = p;
            p = null;
        }
      }
    }
  }

二叉树镜像

 var mirrorTree = function(root) {
    if (root === null) {
        return null;
    }
    const left = mirrorTree(root.left);
    const right = mirrorTree(root.right);
    root.left = right;
    root.right = left;
    return root;
};

二叉树最右边

var rightSideView = function (root) {
  if (!root) {
    return [];
  }

  const queue = [root];
  const res = [];
  while (queue.length) {
    let size = queue.length;
    for (i = 0; i < size; i++) {
      let node = queue.shift();
      if (i === size - 1) {
        res.push(node.val);
      }
      if (node.left) {
        queue.push(node.left);
      }
      if (node.right) {
        queue.push(node.right);
      }
    }
  }
  return res;
};

N叉树层次遍历

var levelOrder = function(root) {
    if(!root) return [];
    let res = [];
    let queue = [];
    queue.push(root);
    while(queue.length){
        let size = queue.length;
        let level = [];
        while(size--){
            let node = queue.shift();
            level.push(node.val);
            for(let child of node.children){
                if(child){
                    queue.push(child);
                }
            }

        }
        res.push(level);
    }
    return res;
};

解析url 拆分出协议 域名 uri 参数

let url = "http://www.runoob.com/index.php?id=1&image=awesome.jpg"
let vars = url.split("&");
let a = []
for(let s in vars){
    a.push(vars[s].split("=")[1])
}
console.log(a)

反转链表

const reverseList = head => {
    // 定义pre指向头部的null
    // cur指向head
    let [pre, cur] = [null, head];
    let temp;
    while (cur) {
        // 将cur.next保存下
        temp = cur.next;
        // 改变cur的指向
        cur.next = pre;
        // 两指针右移
        pre = cur;
        cur = temp;
    }
    // 到最后,cur指向null,返回pre
    return pre;
};

合并两个有序链表

 var mergeTwoLists = function (l1, l2) {
    const prehead = new ListNode(-1);

    let prev = prehead;
    while (l1 != null && l2 != null) {
      if (l1.val <= l2.val) {
        prev.next = l1;
        l1 = l1.next;
      } else {
        prev.next = l2;
        l2 = l2.next;
      }
      prev = prev.next;
    }

    // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
    prev.next = l1 === null ? l2 : l1;

    return prehead.next;
  };

原型链继承

Grand.prototype.lastName = 'JC';
function Grand() {};
var grand = new Grand();
Father.prototype = grand;
function Father() {    
  this.name = 'hehe';
}
var father = new Father();
Son.prototype = father;
function Son() {};
var son = new Son();

构造函数继承

function Person(name, age, sex) {    
  this.name = name;    
  this.age = age;    
  this.sex = sex;
}
function Student(name, age, sex, grade) {    Person.call(this, name, age, sex);    
 this.grade = grade;
}
var student = new Student('dg', 40, 'male', 18);

共享原型

Father.prototype.lastName = 'Deng';
function Father() {};
function Son() {};
Son.prototype = Father.prototype;
var son = new Son();
var father = new Father();

三数之和

var threeSum = function(nums) {
    let len = nums.length
    if(len<3) return []
    let res = []
    nums.sort((a,b)=>a-b)
    for(let i=0;i<len-2;i++){
        let left = i+1
        let right = len-1
        while(left<right){
            let sum = nums[i]+nums[left]+nums[right]
            if(sum>0){
                right--
            }else if(sum<0){
                left++
            }else{
                res.push(`${nums[i]},${nums[left]},${nums[right]}`)
                // 如果相同则修改
                // left不变,right-- 只会比0小
                // left++,right不变 只会比0大
                left++
                right--
            }
        }
    }
    return [...new Set(res)].map(item=>item.split(','))
};