常见的前端面试手写题

782 阅读4分钟

最近把常见的面试手写题还有我自己面试碰到的总结了一下,话不多说,直接看代码:

bind

Function.prototype.bind = function (_this, ...arg) {
  var self = this;
  return function () {
    return self.apply(ins, ...arg)
  }
};

EventEmitter

class EventEmitter {
  constructor() {
    this.listeners = {};
  }

  on(type, listener) {
    let listeners = this.listeners[type];
    if (listeners === undefined) {
      listeners = [listener]
    } else {
      listeners.push(listener)
    }
  }

  off(type, listener) {
    let listeners = this.listeners[type];
    if (listeners === undefined || listeners.length === 0) {
      return
    } else {
      for (let index = 0; index < listeners.length; index++) {
        var curListener = listeners[index];
        if (curListener === listener) {
          listeners.splice(index, 1)
        }
      }
    }
  }

  emit(type, ...arg) {
    let listeners = this.listeners[type];
    if (listeners === undefined || listeners.length === 0) {
      return
    } else {
      for (let index = 0; index < listeners.length; index++) {
        var curListener = listeners[index];
        curListener(...arg)
      }
    }
  }
}

JsonP

(function (window) {
  var id = 0;
  function jsonP(options) {
    if (!options) return;
    id++;
    var scriptTag = document.createElement('script'),
      container = document.getElementsByTagName('head')[0],
      url = options.url,
      data = options.data || {},
      callback = options.callback,
      funcName = `jsonP${id}`,
      params = [];
    data['callback'] = funcName;
    for (var i in data) {
      params.push(`${i}=${data[i]}`)
    };
    window[funcName] = function (res) {
      callback && callback(res);
      container.removeChild(scriptTag);
      delete window[funcName];
    }
    url += url.indexOf('?') > 0 ? '?' : '&';
    url += ary.join('&');
    scriptTag.type = 'text/javascript';
    scriptTag.src = url;
    container.appendChild(scriptTag);
  };
  window.jsonP = jsonP;
})(window)

Koa 中间件原理

class Koa {
    constructor() {
        this.middleWare = []
    }
    use(fn) {
        this.middleWare.push(fn);
        return this
    }
    listen() {
        // 省去启动服务的步骤,直接让middleWare开始运行
        return compose(this.middleWare)({});
    }
}

function compose(middleWare) {
    return function (ctx) {
        let index = -1;
        function next(i) {
            index = i;
            if (index === middleWare.length) return Promise.resolve()
            let fn = middleWare[index];
            return Promise.resolve(fn(ctx, () => next(i + 1)));
        };
        next(0);
    }
}

module.exports = Koa;

promise实现图片懒加载(无力吐槽出这个题的)

function lazyLoad(src) {
  return new Promise((resolve, reject) => {
    let img = new Image();
    img.onload = function (ev) {
      resolve(ev)
    };
    img.onerror = function (ev) {
      reject(ev)
    };
    img.src = src;
  })
}

promisify 具体用处参见node文档

function promisify(fn) {
  return function (...arg) {
    return new Promise(function (resolve, reject) {
      try {
        res = fn(...arg, (err, ...data) => {
          if (err) {
            reject(err)
          } else {
            if (data.length > 1) {
              resolve([...arg])
            } else {
              resolve(data[0])
            }
          }
        })
      } catch (err) {
        reject(err)
      }
    })
  }
}

数组排序

// 冒泡排序
function bubbleSort(ary) {
    for (var i = 0; i < ary.length; i++) {
        for (var j = 0; j < ary.length - i - 1; j++) {
            if (ary[j + 1] < ary[j]) {
                var temp = ary[j];
                ary[j] = ary[j + 1];
                ary[j + 1] = temp;
            }
        }
    };
    return ary
}

// 快速排序
function quickSort(ary) {
    if (ary.length <= 1) return ary
    var mid = Math.floor(ary.length / 2);
    var midItem = ary.splice(mid, 1)[0];
    var leftAry = [];
    var rightAry = [];
    for (var i = 0; i < ary.length; i++) {
        var cur = ary[i];
        if (cur >= midItem) {
            rightAry.push(cur)
        } else {
            leftAry.push(cur)
        }
    };
    return quickSort(leftAry).concat([midItem], quickSort(rightAry))
}

vue 双向绑定

const input = document.getElementById('id');

const obj = { val: '' };

Object.defineProperty(obj, 'val', {
  get() {
    return input.value
  },
  set(val) {
    input.value = value
  }
});

input.oninput = function (ev) {
  const val = ev.target.value;
  obj.val = val;
}

手写Promise(关于这个的文章实在太多了,这里只写下能够完成链式调用的Promise)

class Promise {
  constructor(excutor) {
    this.value = null;
    this.reason = null;
    this.status = 'pending';
    this.onFullfilledCallback = [];
    this.onRejectedCallback = [];
    const resolve = (value) => {
      if (this.status === 'pending') {
        this.value = value;
        this.status = 'resolved';
        this.onFullfilledCallback.forEach(fn => {
          fn(this.value);
        })
      }
    };
    const reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status = 'rejected';
        this.onRejectedCallback.forEach(fn => {
          fn(this.reason)
        })
      }

    }
    try {
      excutor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
  then(onFullfilled, onRejected) {
    onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => reason;
    return new Promise((resolve, reject) => {
      if (this.status === 'resolved') {
        setTimeout(() => {
          try {
            let res = onFullfilled(this.value);
            resolve(res)
          } catch (err) {
            reject(err)
          }
        });
      };
      if (this.status === 'rejected') {
        setTimeout(() => {
          try {
            let res = onRejected(this.reason);
            resolve(res)
          } catch (err) {
            reject(err)
          }
        });
      }
      if (this.status === 'pending') {
        this.onFullfilledCallback.push(() => {
          setTimeout(() => {
            try {
              let res = onFullfilled(this.value);
              resolve(res)
            } catch (err) {
              reject(err)
            }
          });
        });
        this.onRejectedCallback.push(() => {
          setTimeout(() => {
            try{
              let res = onRejected(this.reason);
              resolve(res)
            } catch(err){
              reject(err)
            }
          });
        })
      }
    })
  }
};

手写字符串split方法

// 手写字符串split方法

function split(str, separator) {
  var res = [];
  if (!separator) {
    for (var i = 0; i < str.length; i++) {
      res.push(str[i])
    }
  };
  if (typeof separator === 'string' || Object.prototype.toString.call(separator) === '[object RegExp]') {
    var newReg = separator.global ? separator : new RegExp(separator, 'g');
    var startIndex = 0;
    var flag = true;
    while (flag) {
      var resAry = newReg.exec(str);
      if (resAry === null) {
        if (!startIndex) {
          return [str]
        } else {
          res.push(str.slice(startIndex));
          flag = false;
        }
      } else {
        var endIndex = resAry.index;
        var execLength = resAry[0].length;
        res.push(str.slice(startIndex, endIndex));
        flag = true;
        startIndex = endIndex + execLength;
      }
    }
  }
  return res
}

console.log(split('aa xb xbx1c x1cx1 dx1edede', 'x1'));

找出字符串中最多的字符

function findMax(str) {
    var obj = {};
    for (var i in str) {
        var cur = str[i];
        if (obj[cur]) {
            obj[cur] = obj[cur] + 1;
        } else {
            obj[cur] = 1;
        }
    };
    var maxStr = '';
    var maxNum = 0;
    for (var key in obj) {
        if (obj[key] > maxNum) {
            maxNum = obj[key];
            maxStr = key;
        } else if (obj[key] === maxNum) {
            maxStr += key
        }
    };
    return { maxNum, maxStr }
};

数组去重

function unique(ary) {
    let obj = {};
    for (let i = 0; i < ary.length; i++) {
        const item = ary[i];
        if (obj[item]) {
            ary.splice(i, 1);
            i--;
        } else {
            obj[item] = item;
        }
    };
    return ary
};

function unique2(ary) {
    let res = []
    for (let i = 0; i < ary.length; i++) {
        const item = ary[i];
        if (res.indexOf(item) > -1) {
            ary.splice(i, 1);
            i--;
        } else {
            res.push(item)
        }
    }
    return res
}

function unique3(ary) {
    return [...new Set(ary)]
}

console.log(unique3([1, 2, 3, 4, 4, 2, 5]));

数组去重

function flat1(ary) {
  return JSON.parse('[' + JSON.stringify(ary).replace(/\[|]/g, '') + ']')
}

function flat2(ary) {
  while (ary.some(item => Array.isArray(item))) {
    ary = [].concat(...ary)
  };
  return ary
}

class Stack {
  constructor() {
    this.items = []
  }

  push(item){
    this.items.push(item)
  }

  pop(){
    return this.items.pop();
  }

  clear(){
    this.items = []
  }

  get size (){
    return this.items.length
  }

  get isEmpty(){
    return this.items.length === 0
  }
}

队列

class Queue {
  constructor(){
    this.items = []
  }

  enqueue(items){
    this.items.push(items)
  }

  empty(){
    this.items = [];
  }

  dequeue(){
    return this.items.shift();
  }

  get length(){
    return this.items.length;
  }

  get isEmpty(){
    return this.items.length === 0;
  }
}

深拷贝

// deepCopy
function deepCopy1(obj) {
  return JSON.parse(JSON.stringify(obj))
}

function deepCopy2(obj) {
  var res;
  if (Object.prototype.toString.call(obj) === '[object Array]') {
    res = []
  } else {
    res = {}
  };
  if (typeof obj !== 'object') {
    return
  } else {
    for (var i in obj) {
      var cur = obj[i];
      if (typeof cur === 'object') {
        res[i] = deepCopy2(cur)
      } else {
        res[i] = cur
      }
    }
  };
  return res
}

翻转字符串

function reverseStr1(str) {
    return str.split('').reverse().join('');
}

function reverseStr2(str2) {
    var res = '';
    for (var i = str2.length - 1; i > -1; i--) {
        res += str2[i]
    };
    return res
}

防抖节流

const debounce = (fn, time) => {
  let timer;
  return function (...arg) {
    let that = this;
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(that, [...arg])
    }, time);
  }
}

const throttle = (fn, time) => {
  let startTime = 0;
  return function (...arg) {
    let that = this;
    let curTime = new Date();
    if (curTime - startTime >= time) {
      fn.apply(that, [...arg]);
      startTime = curTime
    }
  }
}

inherits方法()

function inherits(constructor, superConstructor) {
  constructor.prototype = Object.create(superConstructor.prototype)
  // node中  使用Object.setPrototypeOf(constructor, superConstructor)
}

最后祝大家找到理想的工作!