前端代码实现的一些记录

393 阅读4分钟

一.实现new/call/apply/bind

1.new的实现

思路:创建一个空对象,让空对象能够访问到构造函数的原型对象,同时执行构造函数

function myNew(fn) {
    let obj={};
    obj._proto_=fn.prototype;
    const args=[...arguments].slice(1);
    fn.apply(obj,args)
    return obj
}

测试代码

function Person(name,age) {
    this.name=name;
    this.age=age;
}
Person.prototype.sayName=function(){
    console.log(this.name)
}

let p1=myNew(Person,'zhangsan',22);
let p2=new Person('lisi',23);
console.log(p1)
p1.sayName()
console.log(p2)
p2.sayName()
2.call/apply的实现

思路:无论是call还是bind都是用来改变this指向的,思路是将函数作为obj的一个属性指向,然后再删除这个属性

Function.prototype.myCall=function (obj) {
    const fn=this;
    let args=[...arguments].slice(1);
    obj.x=fn;
    const result=obj.x(args);
    delete obj.x;
    return result
}

测试代码

let obj={
    name:'zhangsan',
    age:21
};

function sayName() {
    console.log(this.name)
}

sayName();  
sayName.call(obj)
sayName.myCall(obj)

apply是基于call实现的,不同的是获取的参数,这也是为什么call比apply快的原因,代码如下

Function.prototype.myApply=function(obj){
    const fn=this;
    let args=[...arguments].slice(1);
    let result=fn.call(obj,...args);
    return result;
}
3.bind的实现

思路:bind体现了函数式编程的思想,它会返回一个函数,所以可以不立即执行

Function.prototype.myBind=function (obj) {
    const fn=this;
    const context=obj;
    return function () {
        const args=[...arguments]
        fn.apply(context,args)
    }
}

二.防抖和节流

1.防抖

防抖指的是指定时间内只允许函数触发一次,思路是使用定时器,单位时间再触发会清除定时器并且重新触发。
代码

<script>
    const handle=()=>{
        console.log('有防抖触发函数')
    }

    function debounce(fn,delay) {
        let timeout;
        return function () {
            console.log('无防抖情况')
            const context=this;
            let args=[...arguments]
            if(timeout) clearTimeout(timeout);
            timeout=setTimeout(()=>{
                fn.apply(context,args)
            },delay)
        }
    }

    document.getElementById('input').addEventListener('mousemove',debounce(handle,1000))

</script>
2.节流

节流指的是只有到了指定时间才会触发函数,没有到时间就不会触发

<script>
    const handle=()=>{
        console.log('触发函数')
    }

    function throttle(fn,delay) {
        let previous=Date.now();
        let context=this;
        return function () {
            console.log('11111')
            console.log(previous)
            let args=[...arguments];
            let now=Date.now();
            if(now-previous>delay){
                fn.apply(context,args)
                previous=now
            }
        }
    }

    document.getElementById('input').addEventListener('mousemove',throttle(handle,1000))

</script>

三.深/浅拷贝和数组扁平化

1.深/浅拷贝

B浅拷贝A 指的是B和A共享一个堆内存空间,所以B的内容改变也会影响A的内容,因为他们指向同一个地址空间。一般情况下B=A是一种浅拷贝,当然也可以用Object.assign方法
B深拷贝A 则是将对象的内容完全复制。也就是说此时B和A虽然完全相同,但是他们的堆地址空间是完全不一样的。不会互相影响。实现方法B=JSON.parse(JSON.stringify(A),如果让你实现一个的话,你可以向一下这么写

function deepClone(obj) {
    let newobj=Array.isArray(obj)?[]:{};
    for(let key in obj){
        newobj[key]=isObject(obj[key])?deepClone(obj[key]):obj[key]
    }
    return newobj
}
function isObject(obj) {
    return typeof obj==='object'&&obj!==null
}

测试代码

let obj={
    a:1,
    b:{
        c:[1,2,3],
        d:2
    },
    e:3
}

console.log(deepClone(obj))

此外,数组的深拷贝则是可以用sliceconcat方法

2.数组扁平化

什么是扁平化,就是让数组中层层嵌套数组的形式,变成只有一层的情况。例如[1,[2,[3,4]],5]变成[1,2,3,4,5]。实现的方式有很多,一般的手写是递归版的

function  flatArr(arr) {
    let newArr=[];
    arr.forEach((item,i,arr)=>{
        if(Array.isArray(item)){
            newArr=newArr.concat(flatArr(item))
        }else {
            newArr.push(item)
        }
    })
    return newArr
}


console.log(flatArr([1,2,[3,4,5],6,7]))

四.柯里化

1.函数式编程

以下是笔者的理解:我们在中学阶段都知道y=f(x),对于指定的表达式,输入x得到的一定是唯一的y,只存在一对一的关系。函数式编程就是这样,对于指定输入,总是得到唯一的输出。这里笔者只是用到柯里化的时候提一句,其实函数式编程有很多内容,有兴趣的可以进一步了解。或者等待我日后详细更新。

2.柯里化实现

思路:柯里化是将输入多个参数转换为一个参数,其实利用函数式编程中函数能返回函数以及闭包的特性,能够让我们的所有参数得到缓存

//柯里化要实现的效果

function add(a,b){
    return a+b
}
//普通函数接受多个参数,获取计算的结果。例如这里就需要用add(1,2)的方式

//但是当对函数进行柯里化处理后,即let curryAdd=curry(add)后
//可以使用curryAdd(1)(2)()的方式得到结果3

下面是简单的实现方式

function  curry(fn) {
    let allArgs=[];
    return function next() {
        let args=[...arguments];
        if(args.length>0){
            allArgs=allArgs.concat(args)
            return next;
        }else {
            return fn.apply(null,allArgs)
        }
    }
}

测试代码

const add=function () {
    let sum=0;
    for(let i=0;i<arguments.length;i++){
        sum+=arguments[i]
    }
    return sum
}

let curryAdd=curry(add);
curryAdd(1)
curryAdd(2,3)
let result=curryAdd(4)();
console.log(result)