js面试题手写代码

实现一个new操作符

实现一个instacneof操作符

实现Ajax请求

实现深拷贝

手写apply函数

6.手写call函数

7.手写bind函数

8.手写promise (实现异步调用、链式调用.then .catch、API实现:Promise.resolve、Promise.reject、Promise.all、Promise.race)

9.手写深度比较isEqual

 

 

实现一个new操作符

复制代码
function myNew(fn,...args){
    if(typeof fn !== 'function){
        return TypeError('fn must be a function')
    }

    //let obj = {}  创建一个空对象
    //obj.__proto__ = fn.prototype  将空对象的原型 设置为构造函数fn的 prototype 原型属性
    //创建对象,并指定原型,也可以用下面的代码
    let obj = Object.create(fn.prototype)  //Object.create(proto) 创建一个对象,并指定其原型

    //执行构造函数,并改变this指向
    let result = fn.apply(obj,args)
    //let result = fn.call(obj,...args) 

    return typeof result === 'object' ? result : obj

}
复制代码

 

实现一个instacneof操作符

复制代码

/**
* 用法:instanceof 运算符是用检测构造函数的 prototype 属性是否出现在 某个实例对象的原型链上
* 思路:
* 1.通过Object.getPrototypeOf获取 obj 原型
* 2. 循环判断 objPrototype 是否和 构造函数的原型相等;如果相等返回true;
* 3. 如果不相等就继续obj原型上的原型(Object.getPrototypeOf(objPrototype))
* 4. 判断objPrototype是否为 null,如果为null 说明不存在,返回false
*/

function myInstanceof(obj, fn){
    if(typeof fn !== 'function'){
        return false
    }
    //let objPrototype = obj.__proto__;
    let objPrototype = Object.getPrototypeOf(obj)  //Object.getPrototypeOf() 和obj.__proto__可以互换使用,单位了代码的健壮he可维护性,建议使用Object.getPrototypeOf()
    while(true){
        if(objPrototype === null){
            return false 
        }
        if(objPrototype === fn.prototype){
            return true
        }
        //objPrototype = objPrototype.__proto__
        objPrototype = Object.getPrototypeOf(obj)
    }
} 
复制代码

 实现Ajax请求

复制代码
function Ajax(method,url){
        return new Promise((resolve,reject) =>{
            let xhr = new XMLHttpRequest()
            xhr.open(method || 'GET',url,true) //async: true (异步) false(同步)
            xhr.send() //发送
            xhr.onreadystatechange = function(res){
               if(xhr.readyState == 4){
                    if(xhr.status == 200){
                        console.log(xhr.responseText,'xhr.responseText')
                        //成功
                        resolve(JSON.parse(xhr.responseText))
                    }else{
                        //失败
                        reject(xhr.status)
                    }
                }
            }
           
        })
    } 
复制代码

用法:

Ajax('get','https://testdsmobile.iuoooo.com/api/Products?categoryCode=0&limit=20&page=1&sorting=&roleName=&storeId=&areaCode=110108&t=1725414954615')
    .then(res=>{
        console.log('success',res)
    })
    .catch(res=>{
        console.log('err',res)
    })

 实现深拷贝

  1.递归实现深拷贝

复制代码
      function deepCopy(obj){
            if(!obj || typeof obj !== 'object'){
                return obj
            }
            //如果是日期或者正则对象则返回一个新对象
            if(obj instanceof Date){
                return new Date(obj)
            }
            if(obj instanceof RegExp){
                return new RegExp(obj)
            }
            const newObj = Array.isArray(obj) ? [] : {}
            for(let key in obj){
                if(obj.hasOwnProperty(key)){
                    newObj[key] = deepCopy(obj[key])
                } 
            }
            
            return newObj
        }
复制代码

 

上面的实现有缺陷,如果存在循环引用,就会使程序进入无限循环,最后导致堆栈溢出的问题

循环引用:两个对象存在互相引用的关系,形成一个闭环。例如:

复制代码
      var obj2 ={
                name:'ss',
                age:4
        }
        var obj1 ={
                ceshi: '123',
                test: obj2
        }
        obj2.test2 = obj1
      
复制代码

如果对上面的obj1做深度拷贝 const newobj = deepCopy(obj1) 就会出现堆栈溢出的问题

2.深度拷贝优化-解决循环引用

复制代码
/**
* 思路:
* 1.将 obj 放入 map 中,map中key可以是一个对象
* 2.每次递归调用时判断 对象 是否存在 map中,如果存在就不再递归调用了,直接返回数据
*
* */
        function deepCopy(obj,map = new Map()){
            if(!obj || typeof obj !== 'object'){
                return obj
            }
            //如果是日期或者正则对象则返回一个新对象
            if(obj instanceof Date){
                return new Date(obj)
            }
            if(obj instanceof RegExp){
                return new RegExp(obj)
            }
            //判断 obj 是否在map中存在,如果存在就不再递归调用,而是直接返回数据(解决循环引用的问题,导致无限循环)
            if(map.has(obj)){
                return map.get(obj)
            }

            const newObj = Array.isArray(obj) ? [] : {}
            //放入map中,记录当前的对象,避免重复拷贝,循环引用
            map.set(obj,newObj)

            for(let key in obj){
                if(obj.hasOwnProperty(key)){
                    newObj[key] = deepCopy(obj[key],map)
                } 
            }
            
            return newObj
        }
复制代码

 五、手写apply函数

复制代码
            /**
             * 手写 apply
             * 用法: 用于调用一个函数,并指定函数内部this指向,第一个参数是this指向对象,第二个参数是一个数组
             * 思路:
             *  1. 判断 this 是否指向一个函数,只有函数才可以执行
             *  2. 获取传入的context 上下文,也就是我们要把this指向的对象。如果不存在就默认指向window
             *  3. 将当前this 也就是歪不需要执行的函数 绑定到 context 的一个fn属性上
             *  4. 执行 fn 函数, 判断参数args 是否有,如果有将参数参入再执行,如果没有就直接执行fn函数
             *  5. 删除 context 对象的 fn 属性,并将result 返回
             * */
            Function.prototype.myApply = function(context,args){
                if(typeof this !== 'function'){
                    return new TypeError('type error')
                }
                context = context || window
                context.fn = this
                const result = args ? context.fn(...args) : context.fn()
                // 调用完后需要删除,不然context对象会多一个fn属性,不太合适
                delete context.fn

                return result
            }
复制代码

 

六、手写call函数

复制代码
             /**
             * 手写 call
             * 用法:用于调用一个函数,并指定函数的this指向。第一个参数是this指向对象,从第二个参数开始,每个参数依次传入函数
             * 思路:
             *  1. 判断 this 是否是一个函数
             *  2.获取 context 上下文,也就是我们要指向的对象,如果不存在就指向window
             *  3.将当前 this 也就是外部需要执行的函数 绑定到context上一个fn属性上
             *  4.执行fn, 传入...args,确保参数位置正确
             *  5. 删除context 的fn属性
             *  6. 返回函数执行的结果result
             * */
            Function.prototype.myCall = function(context,...args){
                if(typeof this !== 'function'){
                    return new TypeError('type error')
                }
                context = context || window
                context.fn = this
                const result = context.fn(...args)
                delete context.fn
                return result
            }
复制代码

七、手写bind函数

复制代码
             /**
             * 手写 bind
             * 用法:bind() 方法创建一个新函数,在bind() 被调用时,这个新函数的 this 被指定为bind()的第一个参数,其余参数做为新函数的参数,供新函数调用时使用
             * 思路:
             *  1. 判断 this 是否指向一个函数
             *  2. 获取传入的context 上下文,也就是我们要指向的对象。如果不存在就指向window
             *  3. 将当前this 也就是外部需要执行的函数,绑定到context 一个fn 属性上
             *  4. 返回一个函数,供外部调用。执行新的函数时,仍可以传入新的参数
             *  5. 执行在闭包内缓存的 fn 函数,并将两次参数一起传入
             *  7. 返回result (注意:不需要删除fn,如果删除了新函数就只能调用一次了,再次调用就找不到方法了)
             * */
            Function.prototype.myBind = function(context,...args1){
                if(typeof this !== 'function'){
                    return new TypeError('type error')
                }

                context = context || window
                context.fn = this

                return function (...args2){
                    const result = context.fn(...args1,...args2)
                    delete context.fn
                    return result
                }
            }
复制代码

 八、手写promise (实现异步调用、链式调用.then .catch、API实现:Promise.resolve、Promise.reject、Promise.all、Promise.race)

 

复制代码
           /**
             * 手写Promise
             *  1.初始化和异步调用
             *  2.实现链式调用 .then .catch
             *  3. api实现  resolve reject race all
             * 
             * */
            const PENDING = 'pending'
            const FULFILLED = 'fulfilled'
            const REJECTED = 'rejected'
            class MyPromise{
                //定义两个数组,存储回调
                resolveQueue = [];
                rejectQueue = [];
                status = PENDING; // 状态
                value = null
                constructor(executor){
                    const resolve = (val) =>{
                        if(this.status === PENDING){
                            //变更状态
                            this.status = FULFILLED
                            this.value = val
                            //执行所有的回调并清空
                            while(this.resolveQueue.length){
                                const callback = this.resolveQueue.shift() //从前面取
                                callback(val)
                            }
                        }
                        
                    }
                    const reject = (val)=>{
                        if(this.status === PENDING){
                            //变更状态
                            this.status = REJECTED
                            this.value = val
                            //执行所有的回调
                            while(this.rejectQueue.length){
                                const callback = this.rejectQueue.shift()
                                callback()
                            }
                        }
                    }
                    //模拟异步执行
                    const _resolve = (val)=>{
                        setTimeout(()=>{
                            resolve(val)
                        },0)
                    }
                    const _reject =(val)=>{
                        setTimeout(()=>{
                            reject(val)
                        },0)
                    }
                    // new Promise() 时立即执行 executor,并传入resolve 和 reject 两个参数
                    try{
                        executor(_resolve,_reject)
                    }catch(err){
                        reject(err)
                    }
                }
                /**
                 * 实现then 方法
                 * then 里面的回调方法不是立即执行的,而是异步执行的
                 * 实现链式调用:返回一个Promise,并且下一个then能去到上一个then的返回值
                 * */
                // 实现then 方法。 存储回调 (then)
                then(resolveFun,rejectFun){
                    // 返回全新 promise
                    resolveFun = typeof resolveFun === 'function' ? resolveFun : (val)=>val
                    rejectFun = typeof rejectFun === 'function'? rejectFun : (val)=>val
                    return new MyPromise((resolve,reject)=>{
                        
                        //定义一个全新的方法
                        const _resolveFun = (val)=>{
                            //先执行老方法,获取结果,根据结果的类型进行不同的处理
                            //如果结果返回普通值,只需要resolve(普通值)就可以了
                            //如果结果返回的是Promise 对象,就调用.then方法,将resolve和reject传递过去(传递过去后,在外部调用resolve或者reject,就相当于调用里面的resolve 或者 reject)
                            try{
                                const result = resolveFun(val)
                                if(result instanceof MyPromise){ 
                                    result.then(resolve,reject)
                                }else{ //返回的是一个值
                                    resolve(result)
                                }
                            }catch(err){
                                reject(err)
                            }
                            
                        }
                        const _rejectFun = (val)=>{
                            try{
                                const result = rejectFun(val)
                                if(result instanceof MyPromise){
                                    result.then(resolve,reject)
                                }else{
                                    resolve(result)
                                }
                            }catch(err){
                                reject(err)
                            }
                        }
                        
                        //状态判断,不是panding状态立即回调,否则加入队列异步回调
                        if(this.status === PENDING){ //异步的处理
                            //将回调添加各自队列里,等待时机触发执行
                            this.resolveQueue.push(_resolveFun)
                            this.rejectQueue.push(_rejectFun)
                        }else if(this.status === FULFILLED){
                            _resolveFun(this.value)
                        }else {
                            _rejectFun(this.value)
                        }
                    })
                }

                /**
                 *  静态的 resolve 方法
                 * */
                static resolve(data){
                    return new MyPromise((resolve,reject)=> resolve(data))
                }
                /**
                 *  静态的 reject 方法
                 * */
                 static reject(reason){
                    return new MyPromise((resolve,reject)=> reject(reason))
                }
                /**
                 *  静态的 race 方法
                 * */
                static all(promiseArr){
                    if(!Array.isArray(promiseArr)){
                        new TypeError('you must pass an array to all')
                    }
                    const result = []
                    const length = promiseArr.length
                    let count = 0
                    return  new MyPromise((resolve,reject)=>{
                        for(let i = 0; i < length; i++){
                            promiseArr[i].then(res=>{
                                result[i] = res
                                count++;
                                if(count === promiseArr.length){ //最后一个promise
                                    resolve(result)
                                }
                            }).catch(err=>{
                                reject(err)
                            })
                        }
                    })
                }
                /**
                 * 静态的 race 方法
                 **/
                static race(promiseArr){
                    return new MyPromise((resolve,reject)=>{
                        if(!Array.isArray(promiseArr)){
                            throw new TypeError('must pass an array to race')
                        }
                        for(let p of promiseArr){
                            p.then(res=>{
                                resolve(res)
                            }).catch(err=>{
                                reject(err)
                            })
                        }
                    })

                }
            }
复制代码

 九、手写深度比较isEqual  

复制代码
            //判断是否是对象
            function isObject(obj){
                return typeof obj === 'object' && obj !== null
            }
            //全相等
            function isEqual(obj1,obj2){
                if(!isObject(obj1) || !isObject(obj2)){
                    return obj1 === obj2
                }

                //两个都是引用类型
                // 如果传入是两个相同对象,就没必要再做比较
                if(obj1 === obj2){ 
                    return true
                }
                //两个引用类型不相等

                if(Object.keys(obj1).length !== Object.keys(obj2).length){
                    return false 
                }
                for(let key in obj1){
                    let res =  isEqual(obj1[key],obj2[key])
                    if(!res){
                        return false
                    }
                }
                return true
                
            }
复制代码

 

posted @   yangkangkang  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示