面试技巧

关注公众号 jb51net

关闭
IT专业知识 > IT职场规划 > 面试技巧 >

2020年12道高频JavaScript手写面试题及答案

小耿学前端

JavaScript笔试部分

本文分享 12 道高频JavaScript的面试题,包含手写以及常用的正则。

实现防抖函数 (debounce)

防抖函数原理 : 在事件被触发n秒后在执行回调,如果在这n秒内又被触发,则重新计时。

那么与节流函数的区别直接看这个动画实现即可。

手写简化版

//防抖函数
const debounce = (fn,delay)=>{
    let timer = null;
    return (...args)=>{
        clearTimeout(timer);
        timer = setTimeout(()=>{
        fn.apply(this,args)
        },delay);
    };
};

适用场景 :

生存环境请用lodash.debounce

实现节流函数 (throttle)

防抖函数原理:规定在一单位时间内。只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

//手写简化版
//节流函数
const throttle = (fn,delay = 500) =>{
    let flag = true;
    return (...args) =>{
        if (!flag) return;
        flag = false;
        setTimeout(() => {
        fn.apply(this,args)
        },delay);
    };
};

适用场景:

深克隆 (deepclone)

简单版 :

const newObj = JSON.parse(JSON.stringify(oldObj));

局限性 :
1、他无法实现函数、RegExp等特殊对象的克隆
2、会抛弃对象的constructor,所有的构造函数会指向Object
3、对象有循环引用,会报错

实现Event (event bus)

event bus既是node中各个模块的基石,又是前端组件通信的依赖手段之一,同时涉及了订阅-发布设计模式,是非常重要的基础。

简单版:

class EventEmeitter {
    constructor(){
        this._events = this._events || new Map(); //储存事件/回调键值对
        this._maxListeners = this._maxListeners || 1o;//设立监听上限
    }
}

//触发名为type的事件
EventEmeitter.prototype.emit = function(type,...args){
    let hander;
    //从储存事件键值对的this._events中获取对应事件回调函数
    handler = this._events.get(type);
    if (args.length > 0) {
        hander.apply(this,args);
    }else{
        handler.call(this);
    }
    return true;
};

//监听名为type事件
EventEmeitter.prototype.addListener = function(type,fn) {
    //将type事件以及对应的fn函数放入this._events中储存
    if (!this._events.get(type)) {
        this._events.set(type,fn);
    }
};

实现instanceOf

//模拟 instanceof
function instance_of(L,R){
    var O = R.prototype;//取 R 的显示原型
    L = L.__proto__;//取 L 的隐式原型
    while (true) {
        if (L === null) return false;
        if (O === L)
        // 这里重点 : 当 O 严格等于 L 时,返回 true
        return true;
        L = L.__proto__;
    }
}

模拟new

new操作符做了这些事:

// objectFactory(name,'cxk','18')
function objectFactory(){
    const obj = new object();
    const Constructor = [].shift.call(arguments);
    
    obj.__proto__ = Constructor.prototype;
    
    const ret = Constructor.apply(obj,arguments);
    
    return typeof ret === "object" ? ret : obj;
}

实现一个call

call做了什么 :

//模拟 call bar.mycall(null);
//实现一个call方法;
Function.prototype.myCall = function(context){
    //此处没有考虑context非object情况
    context.fn = this;
    let args = [];
    for (let i = 1,len = arguments.length,i < len; i++){
        args.push(arguments[i]);
    }
    context.fn(...args);
    let result = context.fn(...args);
    delete context.fn;
    return result;
};

实现apply方法

apply原理与call很相似,不多獒数

//模拟 apply
Function.prototype.myapply = function(context,arr){
    var context = Object(context) || window;
    context.fn = this;
    
    var result;
    if (!arr){
        result = context.fn();
    }else{
        var args = [];
        for (var i = 0,len = arr.length;i < len; i++){
            args.push("arr["+ i +"]");
        }
        result = eval("context.fn("+ args + ")");
    }
    delete context.fn;
    return result;
}

实现bind

实现bind要做什么

 

// mdn的实现
if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
          return fToBind.apply(this instanceof fBound
                 ? this
                 : oThis,
                 // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 维护原型关系
    if (this.prototype) {
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();

    return fBound;
  };
}

详解请移步JavaScript深入之bind的模拟实现 #12

模拟Object.create

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
// 模拟 Object.create

function create(proto) {
  function F() {}
  F.prototype = proto;

  return new F();
}

模拟Object.create

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

// 模拟 object.create

function create(proto){
    function F(){
        F.prototype = proto;
        
        return new F();
    }
}

解析 URL Params为对象

let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)

/* 结果
{ user: 'anonymous',
  id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
  city: '北京', // 中文需解码
  enabled: true, // 未指定值得 key 约定为 true
}
*/

转化为驼峰命名

var s1 = "get-element-by-id"

//转化为 getElementById

var f = function(s){
    return s.replace(/-\w/g,function(x){
      return x.slice(1).toUpperCase(); 
    })
}

本文主要是一些基础知识,希望能帮助那些基础不太好的同行们。加油~~~~~~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。