2019-大齡前端如何準備面試之手寫題

前言

今年和以往一個區別就是有一些大廠增長了手寫前端邏輯 或者 收集算法這個環節。前端的一些語言特性,異步控制相對好準備,大多都是Promise的使用外加遞歸。算法就相對來講難準備一些。須要一個長時間的積累。本文分幾類來舉例。拋轉引玉。javascript

前端語言特性

這部分主要考察對於js語言自己的理解,如this,原型鏈的理解,instanceOf,new關鍵字等。前端

  • 實現function的bind 函數
Function.prototype.mybind = function(context, ...args) {
    let fun = this;
    function bound(...args2) {
        let self = this instanceof bound ? this : context;

        return fun.apply(self, args.concat(args2));
    }
    bound.prototype = Object.create(fun.prototype);
    return bound;
};
複製代碼
  • 實現InstanceOf(考察原型鏈的理解)
function isInstanceOf(child, fun) {
    if (typeof fun !== "function") {
        throw new TypeError("arg2 fun is not a function");
    }
    if (child === null) {
        return false;
    }
    if (child.__proto__ !== fun.prototype) {
        return isInstanceOf(child.__proto__, fun);
    }
    return true;
}
複製代碼
  • 實現new 這個關鍵字函數(考察new的過程)
function myNew(fun, ...arg) {
    if (typeof fun !== "function") {
        throw new TypeError(" fun is not a function");
    }
    let obj = {};
    Object.setPrototypeOf(obj, fun.prototype);
    fun.apply(obj, arg);
    return obj;
}
複製代碼
  • 實現JSON.parse函數
function JSONParse(strs) {
    if (strs === "" || typeof strs !== "string") {
        throw new SyntaxError("JSONParse error");
    }
    if (strs[0] === "{") {
        let obj = {};
        if (strs[strs.length - 1] == "}") {
            let fields = strs.substring(1, strs.length - 1).split(",");
            for (let field of fields) {
                let index = field.indexOf(":");
                let temp = [];
                if (index !== -1) {
                    temp[0] = field.substring(0, index);
                    temp[1] = field.substring(index + 1, field.length);
                }
                let key = temp[0].substring(1, temp[0].length - 1);
                let value = parseValue(temp[1]);
                //if (value !== undefined) {
                obj[key] = value;
                //}
            }
        }
        console.log("prase:", obj);
        return obj;
    }
    if (strs[0] === "[") {
        if (strs[strs.length - 1] == "]") {
            let result = [];
            let fields = strs.substring(1, strs.length - 1).split(",");
            for (let field of fields) {
                result.push(parseValue(field));
            }
            return result;
        }
    }
}
複製代碼
  • 實現JSON.stringify函數
function JSONStringify(obj) {
    if (
        obj === undefined ||
        obj === null ||
        typeof obj === "string" ||
        typeof obj === "boolean" ||
        typeof obj === "number"
    ) {
        return obj;
    }
    if (typeof obj === "function") {
        return "";
    }
    if (Array.isArray(obj)) {
        let result = [];
        for (let i = 0; i < obj.length; i++) {
            result.push(JSONStringify(obj[i]));
        }
        return "[" + result.join(",") + "]";
    } else {
        let result = [];
        for (let key in obj) {
            result.push(`"${key}":${JSONStringify(obj[key])}`);
        }
        return "{" + result.join(",") + "}";
    }
}
複製代碼
  • 實現一個繼承(原型鏈)
function myExtends(parent, child) {
    function nop() {}
    nop.prototype = parent.prototype;
    child.prototype = new nop();
}
複製代碼

前端工具類

這部分都是前端的一些高階函數(閉包)。一般用來解決一些通用的問題。這個思想和J2EE中的(面向切面編程)AOP很是類似。例如debounce,memorizejava

  • 實現debounce函數
debounce(fun, delay, immediate) {
       let timer = null;
       return (...args) => {
           if (timer) {
               clearTimeout(timer);
           } else {
               timer = setTimeout(() => {
                   fun.apply(this, args);
               }, delay);
           }
       };
   }
複製代碼
  • 實現throttle函數
throttle(fun, delay, immediate) {
       let flag = false;
       return (...args) => {
           if (!flag) {
               flag = true;
               setTimeout(() => {
                   fun.apply(this, args);
                   flag = false;
               }, delay);
           }
       };
   },
複製代碼
  • 實現memeorize函數,能夠緩存函數的執行結果。在第二次調用以後會加速。
memeorize(fun) {
      let cache = {};
      return (...args) => {
          const key = args.toString();
          if (cache[key]) {
              return cache[key];
          }
          let value = fun.apply(this, args);
          cache[key] = value;
          return value;
      };
  }
複製代碼
  • 實現 promisy 函數。將一個callback的函數轉化爲promise 鏈式調用。
promisy(fun) {
      return (...args) => {
          return new Promise((resolve, reject) => {
              try {
                  fun(...args, resolve);
              } catch (e) {
                  reject(e);
              }
          });
      };
  }
  //使用方法
  fun(arg1,callback);
  let promisey = promisy(fun);
  promisey().then((res)=>());
複製代碼
  • 實現curry化。這是函數式編程的概念。柯里化。將多個參數的函數調用分佈來調用。
currying(fun) {
      function helper(fn, ...arg1) {
          let length = fn.length;
          let self = this;
          return function(...arg2) {
              let arg = arg1.concat(arg2);
              if (arg.length < length) {
                  return helper.call(self, fn, ...arg);
              }
              return fn.apply(this, arg);
          };
      }
      return helper(fun);
  }
  //例子
  function add(a, b) {
      return a + b;
  }
  let curryadd = util.currying(add);
  let add1 = curryadd(1);
  t.is(add1(2), 3);
複製代碼
  • 以千分位格式化數字。輸入123456,輸出123,456
formatNumber(number) {
      if (typeof number !== "number") {
          return null;
      }
      if (isNaN(number)) {
          return null;
      }

      let result = [];
      let tmp = number + "";
      let num = number;
      let suffix = "";
      if (tmp.indexOf(".") !== -1) {
          suffix = tmp.substring(tmp.indexOf(".") + 1);
          num = parseInt(tmp.substring(0, tmp.indexOf(".")));
      }
      while (num > 0) {
          result.unshift(num % 1000);
          num = Math.floor(num / 1000);
      }
      let ret = result.join(",");
      if (suffix !== "") {
          ret += "." + suffix;
      }
      return ret;
  }
複製代碼

前端邏輯控制類

這類問題大可能是遞歸外加promise的理解。你們能夠着重看看promise的使用。掌握了promise仍是很好解決這些問題的。 -實現一個sleep的函數。sleep(3000).then(()=>{})git

function sleep(delay){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
          resolve()
      },delay);
    })
}
複製代碼

-使用XMLHttpRequest 實現一個Promise的ajaxgithub

function myRequest(url, method, params) {
  return new Promise((resolve, reject) => {
      let xhr = new XMLHttpRequest();
      xhr.open(method, url);
      xhr.onreadystatechange = () => {
          if (xhr.readyState != 4) {
              return;
          }
          if (xhr.state === 200) {
              resolve(xhr.response);
          }
      };
      xhr.addEventListener("error", e => {
          reject(error);
      });
      xhr.send(params);
  });
}
複製代碼

-用promise 實現一個lazyman. LazyManAsync("Hank").sleepFirst(5).eat("supper"); LazyManAsync("Hank").sleep(10).eat("dinner")面試

export function LazyManAsync(name) {
  return new LazyManFactory(name);
}

function LazyManFactory(name) {

  this.tasks = [];
  this.tasks.push(() => {
      return new Promise((resolve, reject) => {
          console.log("hi", name);
          resolve();
      })
  });
  setTimeout(() => {
      this.run();
  }, 0);

}

LazyManFactory.prototype.run = function () {

  if (this.tasks.length === 0) {
      return;
  }
  let task = this.tasks.shift();

  task().then(() => {
      this.run();
  }).catch(() => {
      this.run();
  })
}

LazyManFactory.prototype.sleep = function (time) {
  this.tasks.push(() => {
      return new Promise((resolve, reject) => {
          setTimeout(() => {
              resolve();
          }, time * 1000)
      })
  })
  return this;
}


LazyManFactory.prototype.eat = function (name) {
  this.tasks.push(() => {
      return new Promise((resolve, reject) => {
          console.log("eat:", name);
          resolve();
      })
  })
  return this;
}


LazyManFactory.prototype.sleepFirst = function (time) {
  this.tasks.unshift(() => {
      return new Promise((resolve, reject) => {
          setTimeout(() => {
              resolve();
          }, time * 1000)
      })
  })
  return this;
}
複製代碼

算法類

這類通常涉及到算法。這部分短期很差準備,建議能夠做爲長期戰略來複習。這裏暫時只列出很是高頻的簡單題。實際面試中有可能難度大大超過下面。ajax

  • 兩數之和
const twoSum = function(arys, target) {
  if (!Array.isArray(arys)) {
      throw new TypeError("arg1 is not a array");
  }
  const map = new Map();
  for (let i = 0; i < arys.length; i++) {
      let num = target - arys[i];
      if (map.get(num) !== undefined) {
          return [map.get(num), i];
      }
      map.set(arys[i], i);
  }
  return [];
};
複製代碼
  • 快速排序
const quickSort = function(ary = [], start = 0, end = ary.length - 1) {
  if (!Array.isArray(ary)) {
      throw new TypeError("arg1 is not a array");
  }
  if (start >= end || isNaN(start) || isNaN(end)) {
      return;
  }
  let index = partition(start, end);
  quickSort(ary, start, index - 1);
  quickSort(ary, index + 1, end);
  function partition(left, right) {
      let priviot = ary[right];
      let k = left - 1;
      for (let i = left; i <= right - 1; i++) {
          if (ary[i] <= priviot) {
              swap(++k, i);
          }
      }
      swap(++k, right);
      return k;
  }
  function swap(i, j) {
      let temp = ary[i];
      ary[i] = ary[j];
      ary[j] = temp;
  }
};
複製代碼
  • 二分查找.給定一個排序好的數組,用二分查找的辦法找出目標元素。
const binarySearch = function(ary, target) {
  if (!Array.isArray(ary)) {
      throw new TypeError("arg1 is not a array");
  }
  let start = 0,
      end = ary.length - 1;
  while (start <= end) {
      let mid = Math.floor(start + (end - start) / 2);
      if (ary[mid] === target) {
          return mid;
      } else if (ary[mid] < target) {
          start = mid + 1;
      } else {
          end = mid - 1;
      }
  }
  return -1;
};
複製代碼
  • 數組洗牌。將數組中的數字,打亂順序,保證每一個位置的機率相等。
const flush = function(num = []) {
  for (let i = 0; i < num.length; i++) {
      let index = Math.floor(Math.random() * (num.length - 1));
      let temp = num[i];
      num[i] = num[index];
      num[index] = temp;
  }
};
複製代碼
  • 斐波那契數列 實現函數 f(n) = f(n-1)+f(n-2)
const f = (n)=>{
    if(n<0){
      return 0;
    }
    if(n === 0 ){
        return 1;
    }
    return f(n-1)+f(n-2);
    
}
複製代碼
  • 集合的子集.求一個函數全部的子集。好比集合[A,B]。輸出[],[A],[B],[A,B]
var subsets = function(nums) {
  
  let result = [];
  function dfs(index,ans){
      let ans2 = ans.concat();
      ans2.push(nums[index]);
      if(index === 0){
          result.push(ans);
          result.push(ans2);
          return;
      }else{
          dfs(index-1,ans);
          dfs(index-1,ans2);
      }
  }
  dfs(nums.length-1,[]);
  return result;
};
複製代碼

總結

文中出現的題目,本人在github上總結了一個項目turtle-rock.若是您以爲幫助了到你,請幫忙給個star。你的star,是我寫做的動力。算法

說明

因爲本人水平所限,不免有所疏漏。若是有錯誤之處,請評論,我會及時回覆並修改。本文是大齡前端系列之手寫題。編程

2019大齡前端如何準備面試?數組

相關文章
相關標籤/搜索