面试常见手写JS实现代码

206 阅读2分钟

最近自己撸了几个手写实现代码,记录一下。

节流函数

所谓节流函数,可以理解为一头鲸鱼定期要浮出水面呼吸,就是在固定的时间间隔内,无论执行多少次函数,都只会在间隔结束时执行一次。

function throttle(fn,delay){
    let timer = null;
    return function(){
        let context = this;
        let args = arguments;
        if(timer){
            return;
        }
        timer = setTimeout(function(){
            fn.apply(context,args);
            timer = null
        },delay)
    }
}

又或者用时间戳来判断

function throttle(fn,delay){
    let pre = Date.now();
    return function(){
        let context = this;
        let args = arguments;
        let now = Date.now();
        if(now-pre >= delay){
            fn.apply(context,args);
            pre= Date.now();
        }
    }
}


防抖函数

防抖函数,可以理解为在规定的时间内未执行第二次,则执行函数,否则就重置计时器,从新开始计算

function debounce(fn,delay){
    let timer = null;
    return function(){
        let context = this;
        let args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function(){
            fn.apply(context,args);
        },delay)
    }
}

深拷贝方法

function deepClone(obj){
    let copy = obj instanceof Array ? [] : {};
    for(let i in obj){
        if(obj.hasOwnProperty(i)){
            copy[i] = typeof obj[i] == 'object' ? deepClone(obj[i]) : obj[i]
        }
    }
    return copy;
}


实现一个基本的双向绑定

<!--html-->

<input id="input">
<span id="span"></span>


<!--js-->

let obj = {};
let input = document.getElementById('input');
let span = document.getElementById('span');

//对obj对象里面的text值进行数据劫持,检测到这个值发生变化,则触发get方法和set方法
Object.defineProperty(obj,"text",{
    enumerable : true,
    configurable : true,
    set(newValue){
        input.value = newValue;
        span.innerHtml = newValue;
    },
    get(){
        console.log("捕获到值")
    }
})

//监听键盘事件
input.addEventListener('keyup',function(e){
    obj.text = e.target.value;
})


实现一个new函数

function _new(obj,...rest){
    //基于Obj的原型创建一个新的对象
    let newObj = Object.create(obj.prototype);

    //添加属性到新创建的newObj上,并获取obj函数的执行结果
    let result = obj.apply(newObj,rest);
    
    //如果执行结果有返回值并且是一个对象,返回执行的结果,否则返回创建的新对象
    return typeof result =='object' && result!=null ? result : newObj
}

实现一个数据类型的判断函数

function typeOf(obj){
    return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();
}

let obj = {};
let arr = [];
typeOf(obj) //object
typeOf(arr) //array

实现把Json数据转换成树形结构

let arr = [
    { id: 1, name: "部门1", pid: 0 },
    { id: 2, name: "部门2", pid: 1 },
    { id: 3, name: "部门3", pid: 1 },
    { id: 4, name: "部门4", pid: 3 },
    { id: 5, name: "部门5", pid: 4 },
];
//不用递归的方法
function arr2Tree(data){
    let result = [];
    let map = {};
    data.forEach((item,index)=>{
        item.children = [];
        map[item.id] = item;
        if(item.pid == 0 || item.pid == ''){
            result.push(item);
        }else{
            map[item.pid].children.push(item);
        }
    })
    return result;
}

console.log(arr2Tree(arr));

重写一个call方法

    Function.prototype.myCall = function(context){
        if(typeof this !== 'function'){
            throw new Error('not a function');
        }
        context = context || window;
        context.fn = this;
        let args = [...arguments].slice(1);
        let result;
        result = context.fn(...args);
        delete context.fn;
        return result;
    }

重写一个apply方法

    Function.prototype.myApply = function(context){
        if(typeof this !== 'function'){
            throw new Error('not a function');
        }

        context = context || window;
        context.fn = this;
        let args = arguments[1],result = '';
        if(args){
            result = context.fn(...args);
        }else{
            result = context.fn;
        }
        delete context.fn;
        return result;

    }