前端冲刺大厂总结(一)--- 代码篇

2020/5/27 11:25:42

本文主要是介绍前端冲刺大厂总结(一)--- 代码篇,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、前言

大家好!今天来跟大家分享一下冲刺大厂必会的手写代码篇,为了整个篇幅看起来更加舒适一些,今天这篇是搜集的手写代码,下一篇将会总结大厂的高频问答篇。请大家耐心等待ing......

本篇适用人群:正在处于面试阶段的你,或者正准备跳槽,或者平时工作中用到以下代码的......,都欢迎大家来给捧场。

本篇的代码都做了详细的注释,方便大家阅读与理解,如果仍有不理解的可以在评论区讨论一下。

开始让我们的遨游在代码的世界里吧~~~

二、代码篇

1. URL处理

实现效果:将地址栏中的URL中问号后面的内容变为对象的形式:http://www.baidu.com/search?name=li&age=18#student
=>
{HASH: "student", name: "li", age: "18"}

1.1 使用数组中的方法实现

let str = 'http://www.baidu.com/search?name=li&age=18#student';
function urlQuery(str) {
    // 获取?和#的索引
    let askIndex = str.indexOf('?'),
        polIndex = str.indexOf('#'),
        askText = '',
        polText = '';
    //存储最后我们想要的结果    
    let obj = {};
    // 把# ?后面的内容给polText 和 askText
    polIndex != -1 ? polText = str.substring(polIndex + 1) : null;
    askIndex != -1 ? askText = str.substring(askIndex + 1, polIndex) : null;
    //存储到对象中
    polText ? obj['HASH'] = polText : null;
    if (askText) {
         askText.split('&').forEach((item, index) => {
             item = item.split('=');
             obj[item[0]]=item[1];
         });
    }
    return obj;
}
console.log(urlQuery(str));  //=> {HASH: "student", name: "li", age: "18"} 
复制代码

1.2 正则的实现

如果对下面案例中的正则不熟悉,可以查看这个正则篇来学习一下:正则学习

let str = 'http://www.baidu.com/search?name=li&age=18#student';
function urlQuery(str) {
    let obj = {};
    //正则匹配=两边的
    str.replace(/([^=?&#]+)=([^=?&#]+)/g,(_,group1,group2)=>{
        obj[group1]=group2;
    });
    //正则匹配#后边的
    str.replace(/#([^=?&#]+)/g,(_,group1)=>{
        obj['HASH']=group1;
    });            
    return obj;
}
console.log(urlQuery(str));  //=> {name: "li", age: "18", HASH: "student"}
复制代码

2. 数组去重

实现效果:把数组中的重复项去掉。例如: [1,2,3,4,2,1] => [1,2,3,4]

2.1 时间复杂度为O(n^2)的实现

let ary = [1, 2, 2, 3, 4, 2, 1, 3, 1, 3];
function unique(ary) {
    // 双重for循环拿当前项和后面的每一项进行对比
    for (let i = 0; i < ary.length; i++) {
        let item = ary[i];
        for (let j = i + 1; j < ary.length; j++) {
            if (item == ary[j]) {
                //如果当前项和后面的这一项相等,那么末尾一项赋值给后面的这一项,并且长度减一,
                ary[j] = ary[ary.length - 1];
                ary.length--;
                j--;
            }
        }
    }
    return ary;
}
console.log(unique(ary));
复制代码

2.2 时间复杂度为O(n)的实现

let ary = [1, 2, 2, 3, 4, 2, 1, 3, 1, 3];
function unique(ary) {
    let obj = {};
    for (let i = 0; i < ary.length; i++) {
        let item = ary[i];
        if (obj[item] !== undefined) {
            ary[i] = ary[ary.length - 1];
            ary.length--;
            //解决数组塌陷
            i--; 
        }
        obj[item] = item;
   }
   return ary;
}
console.log(unique(ary));
复制代码

2.3 使用ES6的Set实现

Set 是ES6中一种没有重复项的数据结构:阮一峰老师ES6语法的讲解

let ary = [1, 2, 2, 3, 4, 2, 1, 3, 1, 3];
ary = Array.from(new Set(ary));
console.log(ary);
复制代码

3. 内置new的实现

function Fn(x,y){
    this.x=x;
    this.y=y;
}
function _new(func){
    let params=Array.from(arguments).slice(1);
    let obj=Object.create(func.prototype);
    let result=func.apply(obj,params);
    //如果类中有return并且是引入数据类型,那么返回类中的return
    if(result!==null&&(typeof result==='object'||typeof result==='function')){
         return result;
    }
    return obj;
}
let f1=_new(Fn,10,20);
复制代码

4. 什么情况下输出'ok'

var a = ?;
if (a == 1 && a == 2 && a == 3) {
    console.log('ok');
}
复制代码
//方案一 
var a = {
  n:0,
  toString:function(){
    return ++this.n;
  }
}

//方案二
var a = [1,2,3];
a.toString = a.shift;

//方案三
var i = 0;
Object.defineProperty(window,'a',{
  get(){
    //只有获取a的值,就一定会触发get方法执行
    return ++i;
  }
})
复制代码

5. 内置call

Function.prototype.changeThis = function changeThis(context,...args){
  if(context == null){
    //==:null和undefined在两个等号的时候相等,因为if后面,只要传递进来是undefined或者null都可以
    context = window;
  }
  if(typeof context !== 'object' && typeof context !== 'function'){
    context = new context.constructor(context);
  }
  let uniqueKey = `$${new Date().getTime()}`;
  context[uniqueKey] = this;
  let result = context[uniqueKey](...args);
  delete context[uniqueKey];
  return result;
}
复制代码

6. 内置apply

Function.prototype.myApply = function (context) {
    // 如果没有传或传的值为空对象 context指向window
    if (context == null) {
        context = window
    }
    let fn = mySymbol(context)
    context[fn] = this //给context添加一个方法 指向this
    // 处理参数 去除第一个参数this 其它传入fn函数
    let arg = [...arguments].slice(1) //[...xxx]把类数组变成数组,arguments为啥不是数组自行搜索 slice返回一个新数组
    context[fn](arg) //执行fn
    delete context[fn] //删除方法
}

复制代码

7. 内置bind

Function.prototype.bind = function (context) {
    //返回一个绑定this的函数,我们需要在此保存this
    let self = this
    // 可以支持柯里化传参,保存参数
    let arg = [...arguments].slice(1)
    // 返回一个函数
    return function () {
        //同样因为支持柯里化形式传参我们需要再次获取存储参数
        let newArg = [...arguments]
        console.log(newArg)
        // 返回函数绑定this,传入两次保存的参数
        //考虑返回函数有返回值做了return
        return self.apply(context, arg.concat(newArg))
    }
}

复制代码

8. 防抖

函数的防抖debounce:不是某个事件触发就去执行函数,而是在指定的时间间隔内,执行一次,减少函数执行的次数。(在一定时间只能只能执行一次)

/*
 * @Parma
 *   func要执行的函数 
 *   wait间隔等待的时间 
 *   immediate在开始边界还是结束边界触发执行(true在开始边界)
 * @return
 *   可被调用的函数
 * by 不要情绪 on 2020/05/24
*/
function debounce(func, wait, immediate) {
     let result = null,
         timeout = null;
    return function (...args) {
        let context = this,
            now = immediate && !timeout;
       clearTimeout(timeout); //重点:在设置新的定时器之前,要把之前设置的定时器都清除,因为防抖的目的是等待时间内,只执行一次
       timeout = setTimeout(() => {
            if (!immediate) result = func.call(context, ...args);
            clearTimeout(timeout);
            timeout = null;
      }, wait);
      if (now) result = func.call(context, ...args);
   }
}
复制代码

9. 节流

函数的节流(throttle):为了缩减执行的频率,但不像防抖一样,一定时间内只能执行一次,而是一定时间内能执行多次

/*
 * throttle:函数节流是为了缩减执行频率,当达到了一定的时间间隔就会执行一次
 *   @params
 *      func:需要执行的函数
 *      wait:设置的间隔时间
 *   @return
 *      返回可被调用的函数
 * by 不要情绪 on 2019/08/20
 */
let throttle = function (func, wait) {
   let timeout = null,
       result = null,
       previous = 0; //=>上次执行时间点
   return function (...args) {
       let now = new Date,
           context = this;
      //=>remaining小于等于0,表示上次执行至此所间隔时间已经超过一个时间间隔
      let remaining = wait - (now - previous);
      if (remaining <= 0) {
           clearTimeout(timeout);
           previous = now;
           timeout = null;
           result = func.apply(context, args);
     } else if (!timeout) {
           timeout = setTimeout(() => {
                previous = new Date;
                timeout = null;
                result = func.apply(context, args);
           }, remaining);
     }
 return result;
  };
};

复制代码

10. 深克隆

不仅把第一级克隆一份给新的数组,如果原始数组中存在多级,那么是把每一级都克隆一份赋值给新数组的每一个级别

function cloneDeep(obj) {
  // 传递进来的如果不是对象,则无需处理,直接返回原始的值即可(一般Symbol和Function也不会进行处理的)
  if (obj === null) return null;
  if (typeof obj !== "object") return obj;

  // 过滤掉特殊的对象(正则对象或者日期对象):直接使用原始值创建当前类的一个新的实例即可,这样克隆后的是新的实例,但是值和之前一样
  if (obj instanceof RegExp) return new RegExp(obj);
  if (obj instanceof Date) return new Date(obj);

  // 如果传递的是数组或者对象,我们需要创建一个新的数组或者对象,用来存储原始的数据
  // obj.constructor 获取当前值的构造器(Array/Object)
  let cloneObj = new obj.constructor;
  for (let key in obj) {
    // 循环原始数据中的每一项,把每一项赋值给新的对象
    if (!obj.hasOwnProperty(key)) break;
    cloneObj[key] = cloneDeep(obj[key]);
  }
  return cloneObj;
}

复制代码
image
image

11. 深比较

function _assignDeep(obj1, obj2) {
  // 先把OBJ1中的每一项深度克隆一份赋值给新的对象
  let obj = _cloneDeep(obj1);

  // 再拿OBJ2替换OBJ中的每一项
  for (let key in obj2) {
    if (!obj2.hasOwnProperty(key)) break;
    let v2 = obj2[key],
      v1 = obj[key];
    // 如果OBJ2遍历的当前项是个对象,并且对应的OBJ这项也是一个对象,此时不能直接替换,需要把两个对象重新合并一下,合并后的最新结果赋值给新对象中的这一项
    if (typeof v1 === "object" && typeof v2 === "object") {
      obj[key] = _assignDeep(v1, v2);
      continue;
    }
    obj[key] = v2;
  }

  return obj;
}
复制代码

12. 验证手机号

规则:
11 位
第一位是数字 1
第二位是数字 3-9 中的任意一位

let reg = /^1[3-9]\d{9}$/;
console.log(reg.test('13245678945')); //true
console.log(reg.test('1324567895'));  //false
console.log(reg.test('12245678945'));  //false
复制代码

13. 验证是否是有效数字

规则:
开头可以有+ -
整数位:
如果是一位数可以是 0-9 任意数;
如果是多位数,首位不可以是 0;
小数位:如果有小数位,那么小数位后面至少有一位数字,也可以没有小数位

let reg = /^[+-]?(\d|[1-9]\d+)(\.\d+)?$/;
console.log(reg.test('0.2')); //true
console.log(reg.test('02.1'));  //false
console.log(reg.test('20.'));  //false
复制代码

14. 验证密码

规则:
6-16 位组成
必须由数字字母组成

let reg = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[\d(a-z)(A-Z)]{6,16}$/;
复制代码

15. 验证真实姓名

规则:
必须是汉字
名字长度 2-10 位
可能有译名:·汉字

let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10})?$/;
复制代码

16. 验证邮箱

规则: 邮箱的名字以‘数字字母下划线-.’几部分组成,但是-/.不能连续出现也不能作为开头 \w+((-\w+)|(.\w+));
@ 后面可以加数字字母,可以出现多位 @[A-Za-z0-9]+ ;
对@后面名字的补充:多域名 .com.cn ;企业域名 (.|-)[A-Za-z0-9]+)

.com/.cn 等域名 .[A-Za-z0-9]+

let reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/

复制代码

17. 验证身份证号

let reg =/^([1-9]\d{5})((19|20)\d{2})(0[1-9]|10|11|12)(0[1-9]|[1-2]\d|30|31)\d{3}(\d|x)$/i;
复制代码

18. 时间格式字符串

月日不足十位补零
换成年月日的格式

let time = '2020-5-27';
String.prototype.formatTime = function formatTime(template) {
    let arr = this.match(/\d+/g).map(item => {
        return item.length < 2 ? '0' + item : item;
    });
    template = template || '{0}年{1}月{2}日 {3}时{4}分{5}秒';
    return template.replace(/\{(\d+)\}/g, (_, group) => {
        return arr[group] || "00";
    });
};
console.log(time.formatTime()); //=> 2020年05月27日 00时00分00秒
复制代码

19. 字符串中出现次数最多的字符以及次数

let str = "hello";
let ary = [...new Set(str.split(''))];
let max = 0;
let code = ''; for (let i = 0; i < ary.length; i++) {
    //创建正则匹配字符 
    let reg = new RegExp(ary[i], 'g');
    //利用match找出对应字符在中字符串中出现的地方,取匹配的返回数组的长度,即是对应字符串出现的次数 
    let val = str.match(reg).length; //更新出现次数最高的字符与次数 
    if (val > max) {
        max = val;
        code = ary[i];
    } else if (val === max) {
        //处理不同字符出现次数相同的情况  
        code = `${code}、${ary[i]}`;
    }
}
console.log(`出现次数最多的字符是:${code},次数为:${max}`);  //=> 出现次数最多的字符是:l,次数为:2

复制代码

20. 千分符

String.prototype.millimeter = function millimeter() {
    return this.replace(/\d{1,3}(?=(\d{3})+$)/g, value => {
        return value + ',';
    });
};
let str = "2312345638";
str = str.millimeter();
console.log(str); //=>"2,312,345,638"
复制代码

21. 继承(一)原型继承

function Parent() {
    this.x = 100;
}
Parent.prototype.getX = function getX() {
    return this.x;
};

function Child() {
    this.y = 200;
}
Child.prototype = new Parent; //=>原型继承
Child.prototype.getY = function getY() {
    return this.y;
};
let c1 = new Child;
console.log(c1);
复制代码

22. 继承(二)call继承

function Parent() {
    this.x = 100;
}
Parent.prototype.getX = function getX() {
    return this.x;
};
function Child() {
    // 在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的那些东西也就和它没关系了)
    // this -> Child的实例c1
    Parent.call(this); // this.x=100 相当于强制给c1这个实例设置一个私有的属性x,属性值100,相当于让子类的实例继承了父类的私有的属性,并且也变为了子类私有的属性 “拷贝式”
    this.y = 200;
}
Child.prototype.getY = function getY() {
    return this.y;
};
let c1 = new Child;
console.log(c1);

复制代码

23. 继承(三)组合式继承

function Parent() {
    this.x = 100;
}
Parent.prototype.getX = function getX() {
    return this.x;
};
function Child() {
    Parent.call(this);//call
    this.y = 200;
}
Child.prototype = Object.create(Parent.prototype);//原型继承
Child.prototype.constructor = Child;
Child.prototype.getY = function getY() {
    return this.y;
};

let c1 = new Child;
console.log(c1);

复制代码

24. 数组扁平化

[1, 2, [3], [4, 5, [6, [7, 8, 9]]]] => [1,2,3,4,5,6,7,8,9,]

var arr = [1, 2, [3], [4, 5, [6, [7, 8, 9]]]];
function flatten(arr) {
    return arr.reduce((res, next) => {
        return res.concat(Array.isArray(next) ? flatten(next) : next);
    }, []);
}
console.log(flatten(arr));
复制代码

25. 改造代码,输出0-9

闭包的解决方案

for (var i = 0; i< 10; i++){
 setTimeout(() => {
  console.log(i);
    }, 1000)
}
复制代码
for (let i = 0; i< 10; i++){
 setTimeout(() => {
  console.log(i);
    }, 1000)
}
复制代码

26. 冒泡排序

var ary = [3, 1, 5, 2];
function bubble(ary) {
    // 比的轮数,最后一轮不用,前面几轮比完之后,最后那个肯定就是最小的
    for (var i = 0; i < ary.length - 1; i++) {
        //两两进行比较
        for (var j = 0; j < ary.length - 1 - i; j++) {
            if (ary[j] > ary[j + 1]) {
                //解构赋值
                [ary[j], ary[j + 1]] = [ary[j + 1], ary[j]]
            }
        }
    }
    return ary;
}
var res = bubble(ary);
console.log(res);
复制代码

27. 快速排序

function quickSort(ary){
    if(ary.length<1){
        return ary;
    }
   var centerIndex=Math.floor(ary.length/2);
   // 拿到中间项的同时,把中间项从数组中删除掉
   var centerValue=ary.splice(centerIndex,1)[0];
   // 新建两个数组:leftAry,rightAry;把ary中剩余的项,给中间项做对比,如果大项就放到右数组,小项就放到左数组.
   var leftAry=[],rightAry=[];
   for(var i=0;i<ary.length;i++){
        if(ary[i]<centerValue){
            leftAry.push(ary[i]);
        }else{
            rightAry.push(ary[i]);
        }
    }
    return quickSort(leftAry).concat(centerValue,quickSort(rightAry));
}
var ary=[12,15,14,13,16,11];
var res=quickSort(ary);
console.log(res);
复制代码

28. 插入排序

var ary = [34, 56, 12, 66, 12];
function insertSort(ary) {
   //最终排序好的数组盒子
   var newAry = [];
   //拿出的第一项放进去,此时盒子中只有一项,不用个比较
   newAry.push(ary[0]);
   // 依次拿出原数组中的每一项进行插入
   for (var i = 1; i < ary.length; i++) {
      var getItem = ary[i];
      // 在插入的时候需要跟新数组中的每一项进行比较(从右向左)
      for (var j = newAry.length - 1; j >= 0; j--) {
         var newItemAry = newAry[j];
         if (getItem >= newItemAry) {
            // 如果拿出的项比某项大或者相等,就放到此项的后面
            newAry.splice(j + 1, 0, getItem);
            // 插入完毕,不用再继续比较停止循环;
            break;
         }
         if (j == 0) {
            //如果都已经比到第一项了,还没满足条件,说明这个就是最小项,我们之间插入到数组的最前面
            newAry.unshift(getItem);
         }
      }

   }
   return newAry;
}
var res = insertSort(ary);
console.log(res);
复制代码

29. 倒计时案例

let box = document.querySelector('#box'),
    content = document.querySelector('#content'),
    timer = null;
/*
 * 每次页面刷新,必须从服务器拿到时间,才可以进行下面的事情
 *    基于ajax异步去请求,请求成功才做
 *    所以需要放在回调函数里面
 *    为了防止回调地狱问题,可以使用promise
 */

//获取服务器的时间
function getServerTime() {
    return new Promise(resolve => {
        let xhr = new XMLHttpRequest;
        xhr.open('get', './data.json?_=' + Math.random());
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4 && /^(2|3)\d{2}$/.test(xhr.status)) {
                let time = xhr.getResponseHeader('date');
                time = new Date(time);
                resolve(time);
            }
        }
        xhr.send(null);
    });
}

//根据服务器时间计算倒计时
function computed(time) {
    let target = new Date('2020/05/13 18:59:00'),
        spanTime = target - time;
    if (spanTime <= 0) {
        //已经到底抢购的时间点了
        box.innerHTML = '开始抢购吧!'
        clearInterval(timer);
        return;
    }
    //计算出毫秒差中包含多少小时、多少分钟、多少秒
    let hour = Math.floor(spanTime / (60 * 60 * 1000));
    spanTime = spanTime - hour * 60 * 60 * 1000;
    let minutes = Math.floor(spanTime / (60 * 1000));
    spanTime = spanTime - minutes * 60 * 1000;
    let seconds = Math.floor(spanTime / 1000);
    hour < 10 ? hour = '0' + hour : null;
    minutes < 10 ? minutes = '0' + minutes : null;
    seconds < 10 ? seconds = '0' + seconds : null;
    content.innerHTML = `${hour}:${minutes}:${seconds}`;
}

getServerTime().then(time => {
    //获取到服务器的时间后
    computed(time);
    //每隔1秒,累加1
    timer = setInterval(() => {
        time = new Date(time.getTime() + 1000);
        computed(time);
    }, 1000);
});

复制代码

30. 随机验证码

//随机验证码:数字和字母组合,(四个数字和字母)
/* 
=> 1先把验证码准备好
=> 2随机获取相应的索引值
=> 3获取元素
*/
var code = document.getElementById("code");
var btn = document.getElementById("btn");
code.innerHTML = getCode();
btn.onclick = function () {
    code.innerHTML = getCode();
}
function getCode() {
    var str = "qwertyuiopasdfghjklzxcvbnm" + "QWERTYUIOPASDFGHJKLZXCVBNM" + "0123456789";
    var result = "";
    //==> 索引值的范围:0---61
    while (result.length < 4) {
        var index = Math.round(Math.random() * 61);
        var item = str[index];
        if (result.indexOf(item) == -1) {
            result += item;
        }
    }
    return result;
}
复制代码

31. 选项卡实现

<body>
    <div id="app">
        <ul class='list'>
            <li class='active'>css</li>
            <li>js</li>
            <li>html</li>
        </ul>
        <ul class='con'>
            <li class='active'>css样式</li>
            <li>js行为</li>
            <li>HTML结构</li>
        </ul>
    </div>
</body>
复制代码
 * {
    margin: 0;
    padding: 0;
    list-style: none;
  }

 #app {
      width: 500px;
      margin: 20px auto;
 }
 .list {
      overflow: hidden;
      position: relative;
      top: 1px;
 }
 .list li {
      width: 100px;
      float: left;
      border: 1px solid #333;
      text-align: center;
      margin-right: 10px;
      height: 50px;
      line-height: 50px;
      cursor: pointer;
 }
.list li.active {
      background-color: crimson;
      border-bottom-color: crimson;
 }
.con li {
    width: 100%;
    border: 1px solid #333;
    height: 300px;
    line-height: 300px;
    text-align: center;
    background-color: crimson;
    display: none;
 }
 .con li.active {
     display: block;
 }
复制代码
let list = document.querySelector('.list'),
    lists = list.querySelectorAll('li'),
    con = document.querySelector('.con'),
    cons = con.querySelectorAll('li');
for (let i = 0; i < lists.length; i++) {
    lists[i].onclick = function () {
        click(i);
    };
}
function click(index) {
    for (let i = 0; i < lists.length; i++) {
        lists[i].className = '';
        cons[i].className = '';
    }
    lists[index].className = 'active';
    cons[index].className = 'active';
}
复制代码
选项卡
选项卡

三、总结

以上就是目前搜集到的一些手写案例,很感谢大家的阅读。

整理不易,如果感觉写的很不错,可以动动灵活的小手给作者一个赞😂。

写了很多篇了还没有上过热门😪,也想体验一下火的感觉(如果......不知道自己会不会飘起来😂)

最后,请大家耐心等待大厂的高频问答总结二✍️



这篇关于前端冲刺大厂总结(一)--- 代码篇的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程