手写前端必须要掌握的api

花了一天时间手写了前端必须要知道的api,供大家参考。

new、instenceof

    <script>
        function myNew(fn){
            let obj={}
            obj.__proto__=fn.prototype;
            let result=fn.call(obj)
            return result instanceof Object?result:obj;
        }
        function myinstenceof(obj1,obj2){
            while(obj1.__proto__){
                obj1=obj1.__proto__;
                if(obj1===obj2.prototype){
                    return true;
                }
            }
            return false;

        }
        function Person(){
            this.name=2;
            this.age=2;
            // return {name:'hh'}
        }
        function Man(){

        }
        function Dog(){

        }
        Man.prototype=new Person()
        let p=myNew(Person)
        let result=myinstenceof(new Man(),Person)
        let result2=myinstenceof(new Dog(),Person)
        
    </script>

call、apply、bind

<script>
        function add(){
            console.log(arguments)
            return 'aaa'
        }
        Function.prototype.myCall=function (obj,...args){
            if(typeof this!=='function'){
                throw new Error('not function!')
            }
            obj.fn=this
            let result=obj.fn(...args);
            delete obj.fn
            return result
        }
        Function.prototype.myApply=function(obj){
            if(typeof this!=='function'){
                throw new Error('not function!')
            }
            let result;
            if(arguments[1]){
            result=this.call(obj,...arguments[1])
            }else{
                result=this.call(obj)
            }
            return result
        }
        Function.prototype.myBind=function(obj,...args){
            if(typeof this!=='function'){
                throw new Error('not function!')
            }
            let fn=this
            let tempfn=function(){}
            let rfn=function(){
                //说明这个函数当做构造函数来new了
                if(typeof this==='object' && this!==window){
                    fn.apply(this,args.concat(Array.from(arguments)))
                }else{
                    fn.apply(obj,args.concat(Array.from(arguments)))
                }
            }
            tempfn.prototype=fn.prototype;
            rfn.prototype=new tempfn()
            return rfn;
        }
        let obj={name:'小红'}
        let fn2=function(...args){
            console.log(this)
            console.log(Object.prototype.toString.call(args))
        }
        fn2.prototype.sub=function(...args){
            console.log(Object.prototype.toString.call(args))
        }
        add.myApply(obj,['a','b','c'])
        // let testfn3=fn2.myBind(obj,'a','b')
        // testfn3('e','f')
    </script>
</body>

深拷贝(递归)

<script>
        let obj={name:'aaa'}
        let obj1={name:'a',o:{name:'xx',age:12},arr:[{name:'b'},2],d:obj}
        //循环引用测试
        // obj1.d.q=obj;
        function isArrayorObject(ao){
            return Object.prototype.toString.call(ao)==='[object Array]' || Object.prototype.toString.call(ao)==='[object Object]'
        }
        function DeepCopy(obj1,map=new Map()){
            map.set(obj1)
            let obj=isArrayorObject(obj1)?(Array.isArray[obj1]?[]:{}):''
            for(let i in obj1){
                if(isArrayorObject(obj1[i])){
                    if(map.has(obj1[i])){
                        throw new Error('the value of attribute cannot be a parent')
                    }
                    obj[i]=DeepCopy(obj1[i],map)
                }else{
                    obj[i]=obj1[i]
                }
            }
            //回溯之后,删除map中的obj1,不影响其它栈中的判断
            map.delete(obj1)
            return obj
        }
        let obj2=DeepCopy(obj1)
    </script>

排序算法

<script>
        let arr=[1,3,5,9,2,10,4];
        //冒泡排序
        //思想:每次排序把大的数往后排
        function BubbleSort(arr){
            //是否存在交换的值
            let has=false;
            let temp=0;
            for(let i=0;i<arr.length-1;i++){
               for(let j=0;j<arr.length-i-1;j++){
                if(arr[j]>arr[j+1]){
                    temp=arr[j]
                    arr[j]=arr[j+1]
                    arr[j+1]=temp
                    has=true;
                }
               }
               if(!has){
                   break;
               }else{
                   has=false;
               }
            }
        }
        //选择排序
        //思想:每次排序把后面最小的数放到前面
        function SelectSort(arr){
            let mindex;
            let min;
            for(let i=0;i<arr.length-1;i++){
                mindex=i;
                min=arr[i];
                for(let j=i+1;j<arr.length;j++){
                    if(arr[j]<min){
                        mindex=j;
                        min=arr[j]
                    }
                }
                if(mindex!==i){
                    arr[mindex]=arr[i];
                    arr[i]=min;
                }
            }
            
        }
        //插入排序
        //思想:每次排序,把前面当成有序的,后面当成无序的
        function InsertSort(arr){
            let insertIndex;
            let Insert;
            for(let i=0;i<arr.length;i++){
                insertIndex=i;
                Insert=arr[i]
                while(insertIndex>=1&&arr[insertIndex-1]>Insert){
                    arr[insertIndex]=arr[insertIndex-1]
                    insertIndex--
                }
                if(insertIndex!==i){
                    arr[insertIndex]=Insert;
                }
            }
        }
        //希尔排序
        //希尔排序,采用增量法,一开始就可以把后面较小的数往前排
        function ShellSort(arr){
            let insertIndex;
            let Insert;
            for(let gap=Math.floor(arr.length/2);gap>0;gap=Math.floor(gap/2)){
                for(let i=gap;i<arr.length;i++){
                    insertIndex=i;
                    Insert=arr[i];
                    while((insertIndex-gap)>=0&&(arr[insertIndex-gap]>Insert))
                    {   
                        arr[insertIndex]=arr[insertIndex-gap]
                        insertIndex-=gap;
                    }
                    if(insertIndex!==i){
                        arr[insertIndex]=Insert;
                    }
                }
            }
        }
        //快速排序
        //把小的数放到基准数左边,把大的数放到基准数的右边
        function QuickSort(arr,left,right){
            if(left>right){
                return;
            }
            let i=left;
            let j=right;
            let key=arr[left]
            let temp=null;
            while(i<j){
                while(arr[j]>=key&&i<j){
                    j--;
                }
                while(arr[i]<=key&&i<j){
                    i++;
                }
                if(i<j){
                    temp=arr[i]
                    arr[i]=arr[j]
                    arr[j]=temp
                }
            }
            arr[left]=arr[i]
            arr[i]=key;
            QuickSort(arr,left,i-1)
            QuickSort(arr,i+1,right)
        }
        // BubbleSort(arr)
        // SelectSort(arr)
        // InsertSort(arr)
        // ShellSort(arr)
        QuickSort(arr,0,arr.length-1)
    </script>

防抖和节流

<body>
    <input type="text" name="" id="">
    <script>
        function debouce(fn,delay){
            let timer;
            return function(){
                if(timer) clearTimeout(timer)
                timer=setTimeout(()=>{
                    fn()
                },delay)
            }
        }
        function throwttle(fn,wait){
            let date=(new Date()).getSeconds()
            return function(){
                if((new Date()).getSeconds()-date>wait){
                    fn()
                    date=(new Date()).getSeconds()
                }
            }
        }
        let fn=debouce(()=>{
            console.log('防抖了。。。')
        },3000)
        let fn2=throwttle(()=>{
            console.log('节流了。。。')
        },3)
        document.querySelector('input').addEventListener('input',function(){
            // fn()
            fn2()
        })
    </script>
</body>

打印杨辉三角

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin:0;
            padding:0;
        }
        span{
            width:77px;
            text-align: center;
            display: inline-block;
            font-size: 30px;
        }
    </style>
</head>
<body>
    <script>
        function createTriangle(n){
            let arr=[]
            arr.length=n;
            for(let i=0;i<n;i++){
                arr[i]=[]
                arr[i].length=i+1;
            }
            for(let i=0;i<n;i++){
                arr[i][0]=arr[i][arr[i].length-1]=1
            }
            if(n>=3){
                for(let i=2;i<n;i++){
                    for(let j=1;j<arr[i].length-1;j++){
                        arr[i][j]=arr[i-1][j]+arr[i-1][j-1]
                    }
                }
            }
            //打印杨辉三角
            for(let i=0;i<arr.length;i++){
                for(let m=0;m<n-i;m++){
                    document.write('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')
                }
                for(let j=0;j<=i;j++){
                    document.write(`<span>${arr[i][j]}</span>`)
                }
                document.write('<br/>')
            }
        }
        createTriangle(15)
    </script>
</body>
</html>

Promise、Promise.all、Promise.race

<script>
        // function myPromise(fn){
        //     let sucess=[]
        //     let fail=[]
        //     let status='pedding'
        //     let result=null;
        //     resolve=function(value){
        //         status='fulfilled'
        //         result=value
        //         sucess.forEach(v=>v(value))
                
        //     }
        //     reject=function(value){
        //         status='rejected'
        //         result=value
        //         fail.forEach(v=>v(value))
        //     }
        //     try{
        //         fn(resolve,reject)
        //     }catch{
        //         reject('err')
        //     }
        //     this.then=function(fn1,fn2){
        //         if(status==='fulfilled'){
        //             fn1(result)
        //         }
        //         if(status==='rejected'){
        //             fn2(result)
        //         }
        //         if(status==='pedding'){
        //             sucess.push(v=>fn1(v))
        //             fail.push(v=>fn2(v))
        //         }
        //     }
        // }
        
        // new myPromise((resolve,reject)=>{
        //     setTimeout(()=>{
        //         // resolve('成功')
        //         reject('失败')
        //     },1000)
        // }).then(res=>{
        //     console.log(res)
        // },err=>{
        //     console.log(err)
        // })
        
        // function myPromiseAll(promises){
        //     let success=[]
        //     let count=0;
        //     return new Promise((resolve,reject)=>{
        //         function handle(res,index){
        //             success[index]=res;
        //             count=0;
        //             //因为数组中可能有empaty,单纯的用.length进行判断,会算上empty
        //             //使用forEach遍历,不会算上empty
        //             success.forEach(v=>count++)
        //             if(count===promises.length){
        //                 resolve(success)
        //             }
        //         }
        //         promises.forEach((p,index)=>{
        //             //有可能promise有异步方法,为了保证最后结果数组和promises中的顺序保持一致,添加index
        //             p.then(res=>{
        //                 handle(res,index)
        //             },err=>{
        //                 reject(err)
        //             })
        //         })
        //     })
        // }
        // myPromiseAll([new Promise((resolve,reject)=>{setTimeout(()=>{
        //     resolve('第一个结果')
        // },3000)}),Promise.resolve('第二个结果'),Promise.resolve('第三个结果'),Promise.reject('错误')]).then(res=>{
        //     console.log(res)
        // },err=>{
        //     console.log(err)
        // })

        function myPromiseRace(promises){
            return new Promise((resolve,reject)=>{
                promises.forEach(p=>{
                    p.then(res=>{
                        resolve(res)
                    },err=>{
                        resolve(err)
                    })
                })
            })
        }
        let p1=new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve('第一个结果')
            },6000)
        })
        let p2=new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve('第二个结果')
            },3000)
        })
        myPromiseRace([p1,p2]).then(res=>{
            console.log(res)
        },err=>{
            console.log(err)
        })
   </script>

vue2响应式和vue3响应式原理

<script>
        /**
        //使用Object.defineproperty实现vue2响应式原理
        //vue2采用Object.defineproperty带来的问题是无法对数组进行劫持,而且对data中的属性都添加了属性劫持。其实没有必要
        let data={name:'aaa',obj:{name:'bbb',number:1},arr:[1,2,3]}
        class Oberser{
            constructor(data){
                this.oberser(data)
            }
            oberser(data){
                if(!data || !Object.prototype.toString.call(data)==='[object Object]'){
                    return;
                }
                Object.keys(data).forEach(key=>{
                    if(Object.prototype.toString.call(data[key])==='[object Object]'){
                        //如果该属性值是对象,继续深度劫持
                        this.oberser(data[key])
                    }
                    this.defineReactive(data,key,data[key])
                })

            }
            defineReactive(data,key,value){
                //备份this
                let _this=this;
                Object.defineProperty(data,key,{
                    enumerable:true,
                    configurable:true,
                    get(){
                        //当解析模板时,会触发当前属性的get,此时收集依赖
                        //...
                        console.log(`获取了${key}属性值`)
                        //这里使用了闭包
                        return value
                    },
                    set(newValue){
                        if(value!==newValue){
                            value=newValue;
                            console.log(`${key}属性值更新了,我要去更新页面了`)
                            //判断如果设置的是对象的话,继续对该对象的属性进行劫持
                            if(Object.prototype.toString.call(value)==='[object Object]'){
                                _this.oberser(value);
                            }
                            //调用Dep中的Watcher中的update,从而更新页面中用到该属性的地方
                            //...
                        }
                    }
                })
            }
        }
        new Oberser(data)
        **/
    </script>

    <script>
        //vue3响应式原理采用的是proxy,可以对对象或数组进行数据劫持,而且默认只会代理一层。添加属性时也会触发set
        //proxy对数组进行代理时,如果调用数组的push方法添加数值后,会导致第一次添加索引和值,第二次修改length,导致触发了两次更新
        //这里使用WeakMap,而不使用Map的原因是WeackMap是弱引用,也就是如果WeackMap中的对象不被外部所引用了,会清空这个对象的
    
        let reactiveMap=new WeakMap()
        function reactive(data){
            if(typeof data!=='object' ||!data){
                return
            }
            if(reactiveMap.get(data)){
                return reactiveMap.get(data)
            }
            let proxy=new Proxy(data,baseHandler)
            reactiveMap.set(data,proxy)
            return proxy
        }
        const baseHandler={
            get(target,key,receiver){
                let result=Reflect.get(target,key,receiver)
                 //依赖收集
                 console.log(`获取了${key}属性,值是${result}`)
                 track(target,'get',key)
                if(typeof result==='object'){
                    //如果发现取得是对象继续递归代理
                    //这里就体现了使用proxy代理的优越性=>用得到的对象属性才会继续对它代理
                    return reactive(result)
                }
                return result
            },
            set(target,key,value,receiver){
                const oldValue=target[key]
                //对数组和对象新增属性也会走set
                //如果是数组调用push方法添加值时,会触发两次更新:1、添加索引和值(长度在这里被修改了)2、修改长度
                //为防止调用数组push方法,产生两次更新操作,采用下面的做法:
                //调用数组的push方法,第一次是添加的索引和值,因为还没有调用Reflect.set(),所以这里的length还没被改变,此时的hadKey为false
                //调用数组的push方法,第二次是修改的长度,会进入到hadOwn的判断,hasKey为true
                //如果是arr[0]=1,修改索引和值时,hadKey为true
                //如果set的是对象,就会进入到hasOwn的判断,有该属性hadKey为true,无该属性hadKey为false
                let hadKey=Array.isArray(target)&&isIntegerKey(key)?parent(key)<target.length:hasOwn(target,key)
                let result=Reflect.set(target,key,value,receiver);
                if(!hadKey){
                    console.log(`添加了${key}属性,值是${value}`)
                }else if(oldValue!==value){
                    console.log(`${key}属性值更新为${value}`)
                    trigger(target,'set',key,value)
                }

            }
        }
        function effect(fn){
            let effect=createReactiveEffect(fn)
            effect()
        }
        //如果是effect函数嵌套使用的话,为了将依赖和effect一一对应起来,采用栈的结构
        let effectStack=[]
        let activeEffect;
        function createReactiveEffect(fn){
            let effect=function(){
                try{
                    activeEffect=fn;
                    effectStack.push(activeEffect)
                    return fn()
                }finally{
                    effectStack.pop()
                    activeEffect=effectStack[effectStack.length-1]
                }
            }
            return effect
        }
        //将依赖和effect对应起来,并存放到weackmap中
        const depsMap=new WeakMap()
        function track(target,type,key){
            //说明该属性不是在effect中使用的
            if(!activeEffect){return}
            let targetMap=depsMap.get(target)
            if(!targetMap){
                depsMap.set(target,targetMap=new Map())
            }
            let targetSet=targetMap.get(key)
            if(!targetSet){
                targetMap.set(key,targetSet=new Set())
            }
            //防止重复添加,对同一个effect中使用的相同属性,只能算一个
            if(!targetSet.has(activeEffect)){
                targetSet.add(activeEffect)
            }
        }
        function trigger(target,type,key,value){
            //表示对该属性没有收集
            if(!depsMap.get(target)){return}
            let targetMap=depsMap.get(target)
            if(!targetMap){
                return;
            }
            let targetSet=targetMap.get(key)
            if(!targetSet){
                return;
            }
            targetSet.forEach(e=>e())
        }
        //定义一些常用的方法
        //判断是否是索引
        function isIntegerKey(key){
            return parseInt(key) ===key
        }
        function hasOwn(target,key){
            return Object.prototype.hasOwnProperty.call(target,key)
        }

        let data={name:'aaa',obj:{name:'xxx'},age:12}
        let proxy=reactive(data)
        effect(()=>{
            console.log(proxy.obj.name)
            effect(()=>{
                console.log(proxy.name)
            })
            console.log(proxy.age)
        })

    </script>
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值