前端面试手撕代码总结

算法

排序算法

Bubble Sort 冒泡排序 (Time: O(n^2), Space(1))
function bubbleSort(arr){
   var i, j;
   var l = arr.length;
   for(i=0; i<l; i++){
       for(j=0; j<l-1-i; j++){
           if(arr[j]>arr[j+1]){
               let t = arr[j];
               arr[j] = arr[j+1];
               arr[j+1] = t;
            }
        }
   }
   return arr;
}
Insertion Sort 插入排序 (Time: O(n^2), Space(1))

([ <= pivot ], pivot, [ >= pivot ]); Recursion

function insertionSort(arr){  
    let len = arr.length;  
    let i, current, prevIdx;  
    for(i=1; i<len; i++){  
        prevIdx = i-1;  
           current = arr[i];  
           while(prevIdx >= 0 && current < arr[prevIdx]){  
               arr[prevIdx+1] = arr[prevIdx];  
               prevIdx--;  
           }  
           arr[prevIdx+1] = current;  
     }  
   return arr;  
}
Quick Sort 快速排序 (Time: O(n^2), Space(logn)

([ <= pivot ], pivot, [ >= pivot ]); Recursion

function quicksort(arr){  
  var len = arr.length;   
  if(len <= 1){  //  
     return arr;  
   }  
   var pivot =  arr[Math.floor(Math.random()*len + 0)];
   var left = [];  
   var right = [];  
   var mid = [];  
   var i;  
   for(i=0; i<len; i++){  
     if(arr[i]< pivot)  
        left.push(arr[i]);  
     else if(arr[i]> pivot){  
        right.push(arr[i]);  
     }else{  
        mid.push(arr[i]);  
     }  
  }  
  return quicksort(left).concat(mid, quicksort(right));  
}
Merge Sort 合并排序 (Time: O(nlogn), Space: O(n) worst)
    function mergeSort(arr){  
         let len = arr.length;  
         if(len<=1){  
             return arr;  
          }else{  
             let mid = Math.floor(len/2);  
             let left = arr.slice(0, mid);  
             let right = arr.slice(mid, len);  
             return merge(mergeSort(left), mergeSort(right));  
          }  
    }  
    function merge(left, right){  
         let res = [];  
         while(left.length && right.length){  
             if(left[0]<right[0]){  
                 res.push(left.shift());  
             }else{  
                 res.push(right.shift());  
             }  
         }  
         return res.concat(left, right);  
    }

递归与动态规划

斐波那契数列实现

1,1,2,3,5,8...

  1. 递归法
function fibonacci(n) {
    if(n<=0) return;
    if(n<3) return 1;
    return fibonacci(n-1) + fibonacci(n-2);
}
  1. 动态规划法: 子问题函数记录结果直接使用
    p[n] = p[n-1] + p[n-2]
function fibonacciDP(n) {
    if(n<=0) return;
    const p = [1,1];
    if(n<=2) return p[n-1];
    for(let i=2; i<n; i++){
        p[i] = p[i-1] + p[i-2];
    }
    return p[n-1];
}

数据结构

  1. BFS输出
/*function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
*/
var levelOrder = function(root) {
    if(root === null) return [];
    let output = [];
    let data = [root];
    while(data.length !== 0){
        let current = data.shift();
        output.push(current.val);
        if(current.left){
            data.push(current.left);
        }
        if(current.right){
            data.push(current.right);
        }
    }
    return output;
};

JS前端技术

原生Ajax
function ajaxRequest(method, data, withCookie){
  let xhr = new XMLHttpRequest();
  if(!!withCookie){  //允许发送Cookie
      xhr.withCredentials = true;
  }
  let res;
  //httpRequest.readyState: 
  /*0 - UNSENT 代理创建,尚未调用open(); 
    1 - OPENED open()已调用; 
    2 - HEADERS_RECEIVED send()已调用,获得头部状态; 
    3 - LOADING 下载中; 
    4 - DONE 下载完成,responseText属性数据完整.*/
  xhr.onreadystatechange = function(e){
    if(this.readyState == 4 && this.status == 200){
          console.log("success");
          res = this.responseText;
    }
  }
  if(method.toLowerCase() === "get"){
    xhr.open("get", "http://XXX.XXX.com/", true);
    xhr.send();
  }
  if(method.toLowerCase() === "post"){
    xhr.open("post", "http://XXX.XXX.com/", true);
    xhr.send(data); //data为"key=value&key2=value2"
  }
  return res;
}
Jsonp跨域请求
<script type="text/javascript">
  let temp = document.createElement("script");
  temp.type = "text/javascript";
  const callback = function(res){
      alert(res);
  }
  temp.src = "http://XXX.XXX.com/YYY?callback=callback";
</script>
数组去重
const arr = [1,1,1,2,2,3];
//1. Set转array
const arr1 = [... new Set(arr)];
//2. Array.from
const arr2 = Array.from(new Set(arr));
//3.循环加入新数组: 判断 arr3.includes(); arr3.indexOf()===-1 [time: O(n), space: O(n)]
function noDup1(arr){
  let res = [];
  arr.forEach((item)=>{
      if(!res.includes(item)){
          res.push(item);
      }
   });
   return res;
}
//4. sort()比较相邻元素,在原数组删除 arr.splice(start, 1) [time: O(n), space: O(1)]
function noDup2(arr){
  arr = arr.sort();
  let i = 0;
  while(i<arr.length-1){
    if(arr[i]===arr[i+1]){
      arr.splice(i, 1);
    }else{
      i++;
    }
  }   
}
数组扁平化 flatten
/**
forEach遍历数组所有项添加到新的数组
嵌套数组递归
*/
function flatten(arr){
  const res = [];
  arr?.forEach((item)=>{
      if(Array.isArray(item)){
           res = res.push(...flatten(item));
      }else{
          res.push(item);
      }
  });
  return res;
}

/** 
ES2019新增flat方法: 性能最佳
flat(depth), 默认depth=1, depth为原数组(维度-1),不明确深度可用Infinity
*/
array.flat(Infinity);

/**
ES2019新增flatMap((item)=>item)
扁平化二维数组,不改变原数组
*/
arr.flatMap((item)=>item);
判断两个对象是否相同
function isEqualObj2(obj1, obj2) {
    // 排除非object的值
    if (typeof obj1 !== 'object' || typeof obj2 !=='object') return false;
    // 两个null的情况 (typeof null === ‘object')
    if (obj1 === null && obj2 === null) return true;
    // 两个object的key数量不等就直接false
    if (Object.keys(obj1).length !== Object.keys(obj2).length) return false;
    for (key in obj1) {
        // 嵌套对象递归处理
        if (typeof obj1[key] === 'object' && !!obj1[key]) {
          if (typeof obj2[key] === 'object' && !!obj2[key]) {
            return isEqualObj2(obj1[key], obj2[key]);
          }
          return false;
        }
        if (obj1[key] !== obj2[key]) return false;
    }
    return true;
}

关于JSON.stringify

JSON格式
简单值:字符串、数值(十进制)、布尔值和null(NaN, Infinity, -Infinity和undefined都会被转为null)
复合值:符合JSON格式的嵌套对象和数组
JSON内所有属性名和字符串都用双引号“”,每个对象或数组最后一个值后不能有逗号

JSON.stringify (JSON序列化)

  1. undefined 值、函数、Symbol值会被忽略(为null)
  2. 正则对象会被转化为空对象"{}"
  3. 忽略对象的不可遍历的属性(Enumerable为false的忽略)
    第二个参数
  4. 传入一个数组,包含需要被转为字符串的属性名
  5. 传入一个方法,参数为(key, value)可对value进行处理后输出
    a) 对多级嵌套进行递归处理, (参数2的方法也是递归执行)
    b) 如果处理函数返回undefined或没有返回值,则该属性会被忽略
    第三个参数:改善输出字符串的可读性
  6. 传入整数:每行属性前的空格数<= 10
  7. 传入字符串:每行属性前的字符串 <= 10个
    给对象添加toJSON方法,JSON.stringify前会先执行对象的toJSON转换再转成字符串

特别注意:JSON.stringify() 解析循环对象的时候,会报错!需要try catch!!

对象深拷贝 deepClone
//循环拷贝
function deepClone(obj){
    if(typeof obj != "object"){
        return obj;
    }else{
       let newObj={};
       for(let k in obj){
          if(typeof obj[k] != "object"){
             newObj[k] = obj[k];
          }else{
             newObj[k] = deepClone(obj[k]);
          }
      }
      return newObj;
    }
}

//JSON转换:将object序列化为string, 再反序列化为新的object
// 保证obj 没有循环引用问题,obj内的值不设计JSON.stringify说明的那些问题
let objClone = JSON.parse(JSON.stringify(obj));
防抖 debounce (一段时间不触发事件才执行一次handle函数)
function debounce(fn, wait = 1000){
  let timer = null;
  return function(){
    let _this = this;
    if(timer){
      timer = null; 
    }  
    timer = setTimeout(()=>{
      fn.apply(_this, arguments);
    }, wait);  
  }
}  

引申:clearTimeout(timer) 只是停止了回调函数定期执行,timer依旧存在,if(timer) 内依旧执行; 而timer = null释放了内存, (timer) 为false。

节流 throttle(一段时间内只执行一次handle函数)
function throttle(fn, wait = 1000){
  let canRun = true;
  return function(){
    let _this = this;
    if(canRun){
      setTimeout(()=>{
        fn.apply(_this, arguments);
        canRun = true;
      }, wait);
      canRun = false;
    }
  }
}
按秒输出0,1,2,3,4,5
//1: let 块级独立作用域
for(let i=0; i<=5; i++){
    setTimeout(()=>{
      console.log(i);
    }, i * 1000);
}
//2:立即执行闭包函数,定义后立即执行,将变量包裹成局部变量。
for(var i=0; i<=5; i++){
  (function (j){
    setTimeout(()=>{
      console.log(j);
    }, j*1000)
  })(i) 
}
//3: setTimeout第三个参数,作为中间函数执行时的参数
for(var i=0; i<=5; i++){
  setTimeout((j)=>{
    console.log(j);
  }, i*1000, i);
}
函数柯里化curryadd(1)(2)(3)
function curryadd(a){
  return function(b){
    return function(c){
      return a+b+c;
    }
  }
}
new实现:
  1. 新建空对象
  2. proto指向构造函数的prototype
  3. 新对象this绑定为构造函数的this
  4. 执行构造函数返回对象。**
function newFn(fn){
   if(typeof fn !== "function"){
    throw "第一个参数要为function";
  }else{
    let newObj = {};
    newObj._proto_ = fn.prototype;
    let args = [...arguments].slice(1);
    let result = fn.apply(newObj, args);
    const isObj = typeof result === "object" && result !== null;
    const isFunc = typeof result === "function";
    return isObj || isFunc ? result : newObj;
  }   
}
instanceof 实现:

typeof原理:根据JS底层机器码前三位来获取类型信息
000:对象
010:浮点数
100:字符串
110:布尔
1:整数
null在JS设计之初定为全0,因此typeof根据前三位000,判断null为"object"
typeof 可以判断基本类型number, string, boolean, symbol, bigint, undefined 和object, function (class也是function)

介于typeof无法判断具体的object类型,只能用Object.prototype.toString.call()来获取具体的object类型

incetanceof 原理
A instanceof B:检查A的原型链上是否存在B.prototype, 向上查找A的原型链,直到null。

const myInstanceof = (left, right) => {
    // 首先得满足left有__proto__属性,right为callable的函数
    if ((typeof left !== "object" && typeof left !== "function") 
    || left === null 
    || typeof right !== "function") {
        return false;
    }
    const rightPrototype = right.prototype;
    let leftProto= left.__proto__;
    while(true) {
        if(leftProto === null) {
            return false;
        }
        if(leftProto === rightPrototype) {
            return true;
        }
        leftProto = leftProto.__proto__;
    }
}

改变原方法中的this指向:call、apply、bind

call实现
  1. 接收一个以上任意数量的参数
  2. 第一个参数是函数想要作用的对象
Function.prototype.call2 = function (ctx) {
    if (typeof this !== 'function') {
        throw new Error('Function undefined');
    }
    ctx = ctx || window;
    ctx.fn = this;
    const args = [...arguments].slice(1);
    const res = ctx.fn(...args);
    delete ctx.fn;
    return res;
}
apply实现
  1. 接收一到两个参数
  2. 第一个参数是函数想要作用的对象
  3. 第二个参数是包含所有剩余参数的Array
Function.prototype.apply2 = function(target, arr){
    var tar = (target===null) ? window : target;
    tar.fn = this;
    var result;
    if(!arr){
        result = tar.fn();
    }else{
        result = tar.fn(...arr);
    }
    delete tar.fn;
    return result;
};
bind实现
  1. 接收一个以上任意数量的参数
  2. 第一个参数是函数的目标作用对象
  3. call和apply直接返回调用函数的结果,bind只返回改变作用对象后的函数
Function.prototype.bind2 = function (ctx) {
    const fn = this;
    const args = [...arguments].slice(1);
    const F = function () {};
    const fBind = function () {
        return fn.apply(this instanceof fBind ? this : ctx, args.concat(...arguments))
    };
    if (fn.prototype) {
        F.prototype = fn.prototype;
    }
    fBind.prototype = new F();
    return fBind;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容