Javascript收集

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

2018-04-03  本文已影响8人  一只dororo

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

手写一个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);
上一篇下一篇

猜你喜欢

热点阅读