美文网首页Javascript收集
前端面试题总结(专题二)——手写实现功能

前端面试题总结(专题二)——手写实现功能

作者: 一只dororo | 来源:发表于2018-04-03 12:43 被阅读8次

    最近在前端面试,面经是自己的和周围同学的面经整理出来的,持续更新有用的同学加关注嗷

    手写一个js的深克隆(美团、爱奇艺)

    function deepCopy(obj){
        //判断是否是简单数据类型,
        if(typeof obj == "object"){
            //复杂数据类型
            var result = obj.constructor == Array ? [] : {};
            for(let i in obj){
                result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
            }
        }else {
            //简单数据类型 直接 == 赋值
            var result = obj;
        }
        return result;
    }
    

    手写组合继承(美团、爱奇艺、搜狗)

    // 定义一个动物类
    function Animal (name) {
      // 属性
      this.name = name || 'Animal';
      // 实例方法
      this.sleep = function(){
        console.log(this.name + '正在睡觉!');
      }
    }
    // 原型方法
    Animal.prototype.eat = function(food) {
      console.log(this.name + '正在吃:' + food);
    };
    //组合继承
    function Cat(name){
      Animal.call(this);
      this.name = name || 'Tom';
    }
    Cat.prototype = new Animal();
    

    手写一个promise(爱奇艺、搜狐)

    promise是一个构造函数,下面是一个简单实例
    var promise = new Promise((resolve,reject) => {
        if (操作成功) {
            resolve(value)
        } else {
            reject(error)
        }
    })
    promise.then(function (value) {
        // success
    },function (value) {
        // failure
    })
    

    防抖和节流

    scroll 事件本身会触发页面的重新渲染,同时 scroll 事件的 handler 又会被高频度的触发, 因此事件的 handler 内部不应该有复杂操作,例如 DOM 操作就不应该放在事件处理中。

    针对此类高频度触发事件问题(例如页面 scroll ,屏幕 resize,监听用户输入等),有两种常用的解决方法,防抖和节流。

    防抖(Debouncing)

    防抖技术即是可以把多个顺序地调用合并成一次,也就是在一定时间内,规定事件被触发的次数。

    通俗一点来说,先看下面这个简化的例子,这个简单的防抖的例子大概功能就是如果 500ms 内没有连续触发两次 scroll 事件,那么才会触发我们真正想在 scroll 事件中触发的函数。

    // 简单的防抖动函数
    function debounce(func, wait, immediate) {
        // 定时器变量
        var timeout;
        return function() {
            // 每次触发 scroll handler 时先清除定时器
            clearTimeout(timeout);
            // 指定 xx ms 后触发真正想进行的操作 handler
            timeout = setTimeout(func, wait);
        };
    };
     
    // 实际想绑定在 scroll 事件上的 handler
    function realFunc(){
        console.log("Success");
    }
     
    // 采用了防抖动
    window.addEventListener('scroll',debounce(realFunc,500));
    // 没采用防抖动
    window.addEventListener('scroll',realFunc);
    

    完整的防抖动函数:

    // 防抖动函数
    function debounce(func, wait, immediate) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };
     
    var myEfficientFn = debounce(function() {
        // 滚动中的真正的操作
    }, 250);
     
    // 绑定监听
    window.addEventListener('resize', myEfficientFn);
    

    节流(Throttling)

    防抖函数确实不错,但是也存在问题,譬如图片的懒加载,我希望在下滑过程中图片不断的被加载出来,而不是只有当我停止下滑时候,图片才被加载出来。又或者下滑时候的数据的 ajax 请求加载也是同理。

    这个时候,我们希望即使页面在不断被滚动,但是滚动 handler 也可以以一定的频率被触发(譬如 250ms 触发一次),这类场景,就要用到另一种技巧,称为节流函数(throttling)。

    节流函数,只允许一个函数在 X 毫秒内执行一次。与防抖相比,节流函数最主要的不同在于它保证在 X 毫秒内至少执行一次我们希望触发的事件 handler。
    与防抖相比,节流函数多了一个 mustRun 属性,代表 mustRun 毫秒内,必然会触发一次 handler。

    同样是利用定时器,看看下面的简单示例,大概功能就是如果在一段时间内 scroll 触发的间隔一直短于 500ms ,那么能保证事件我们希望调用的 handler 至少在 1000ms 内会触发一次。

    // 简单的节流函数
    function throttle(func, wait, mustRun) {
        var timeout,
            startTime = new Date();
     
        return function() {
            var context = this,
                args = arguments,
                curTime = new Date();
     
            clearTimeout(timeout);
            // 如果达到了规定的触发时间间隔,触发 handler
            if(curTime - startTime >= mustRun){
                func.apply(context,args);
                startTime = curTime;
            // 没达到触发间隔,重新设定定时器
            }else{
                timeout = setTimeout(func, wait);
            }
        };
    };
    // 实际想绑定在 scroll 事件上的 handler
    function realFunc(){
        console.log("Success");
    }
    // 采用了节流函数
    window.addEventListener('scroll',throttle(realFunc,500,1000));
    

    手写原生js实现事件代理,并要求兼容浏览器(腾讯)

    / ============ 简单的事件委托
    function delegateEvent(interfaceEle, selector, type, fn) {
        if(interfaceEle.addEventListener){
        interfaceEle.addEventListener(type, eventfn);
        }else{
        interfaceEle.attachEvent("on"+type, eventfn);
        }
          
        function eventfn(e){
        var e = e || window.event;   
        var target = e.target || e.srcElement;
        if (matchSelector(target, selector)) {
                if(fn) {
                    fn.call(target, e);
                }
            }
        }
    }
    
    function matchSelector(ele, selector) {
        // if use id
        if (selector.charAt(0) === "#") {
            return ele.id === selector.slice(1);
        }
        // if use class
        if (selector.charAt(0) === ".") {
            return (" " + ele.className + " ").indexOf(" " + selector.slice(1) + " ") != -1;
        }
        // if use tagName
        return ele.tagName.toLowerCase() === selector.toLowerCase();
    }
    //调用
    var odiv = document.getElementById("oDiv");
    delegateEvent(odiv,"a","click",function(){
        alert("1");
    })
    

    手写Function.bind函数(腾讯、爱奇艺)

    if(!Function.prototype.bind){
        Function.prototype.bind = function(oThis){
            if(typeof this !=="function"){ //如果不函数抛出异常
                throw new TyperError("")
            }
            var aArgs = Array.prototype.slice.call(arguments,1),  //此处的aArgs是除函数外的参数
                fToBind = this,//要绑定的对象
                fNOP = function(){},
                fBound = function(){
                    return fToBind.apply(
                        this instanceof fNOP ? this:oThis||this,aArgs.concat(Array.prototype.slice.call(arguments)));
                        )
                };
            fNOP.prototype = this.prototype;
            fBound.prototype = new fNOP();
            return  fBound;
        }
    }
    

    手写AJAX(腾讯)

    创建XMLHttpRequest对象

    指定响应函数

    打开连接(指定请求)

    发送请求

    创建响应函数

    var xmlhttp=null;//声明一个变量,用来实例化XMLHttpRequest对象
    if (window.XMLHttpRequest)
      {
      xmlhttp=new XMLHttpRequest();// 新版本的浏览器可以直接创建XMLHttpRequest对象
      }
      
    else if (window.ActiveXObject)
      {
      xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");// IE5或IE6没有XMLHttpRequest对象,而是用的ActiveXObject对象
      }
      
      
    if (xmlhttp!=null)
      {
      xmlhttp.onreadystatechange=state_Change;//指定响应函数为state_Change
      xmlhttp.open("GET","/example/xdom/note.xml",true);//指定请求,这里要访问在/example/xdom路径下的note.xml文件,true代表的使用的是异步请求
      xmlhttp.send(null);//发送请求
      } 
    else
      {
      alert("Your browser does not support XMLHTTP.");
      }
    
    //创建具体的响应函数state_Change
    function state_Change()
    {
    if (xmlhttp.readyState==4)
      {
      if (xmlhttp.status==200)
        {
        // 这里应该是函数具体的逻辑
        }
      else
        {
        alert("Problem retrieving XML data");
        }
      }
    }
    

    手写XMLHttpRequest

    var xhr = new XMLHttpRequest();
    xhr.open("GET","/api",false);
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4){
            if(xhr.status == 200){
                alert(xhr.responseText);
            }
        }
    }
    xhr.send(null);
    

    相关文章

      网友评论

        本文标题:前端面试题总结(专题二)——手写实现功能

        本文链接:https://www.haomeiwen.com/subject/ihvqhftx.html