前端面试常见手写题,包含手写实现 new instanceof 浅拷贝 深拷贝 call/apply 防抖节流 promise 函数柯里化 快速排序、桶排序、计数排序、promise.all等

实现一个new操作符

//实现一个new操作符
function myNew(fn,...args){
    if(typeof fn !== 'function') {
        throw ('fn is not a function')
    }

    //将对象的原型设置为fn的prototype
    let res=Object.create(fn.prototype)

    //使用 apply 执行构造函数 并传入参数 arguments 获取函数的返回值
    let result=fn.apply(res,args)

    return result
}

实现instanceof

//实现instanceof

function myInstanceof(left,right){
    //如果左边不是引用数据类型
    if(typeof left !== 'object' || left === null) return false

    //获取左边的原型
    let proto=Object.getPrototypeOf(left)

    while(true){
        //查找到底了
        if(proto==null) return false
        //相等
        if(proto==right.prototype) return true
        //顺着原型链继续查找
        proto=Object.getPrototypeOf(proto)
    }
}

实现浅拷贝

function shallowClone(obj){
    //如果是基础数据类型,直接返回
    if(typeof obj !== 'object' || obj==null) return obj

    //如果是引用数据类型,新开辟一个地址,拷贝一层
    let newObj=Array.isArray(obj) ? [] : {}

    for(let key in obj){
        newObj[key]=obj[key]
    }

    return newObj
}

实现深拷贝

function deepClone(obj){
    let cloneObj=Array.isArray(obj)?[]:{};

    //遍历其中的key
    for(let key in obj){
        if(typeof obj[key]==='object'){
            //递归
            cloneObj[key]=deepClone(obj[key]);
        }else{
            cloneObj[key]=obj[key];
        }
    }
    return cloneObj
}
//改进版实现深拷贝
function deepClone(obj,hash=new WeakMap()){
    //判断是否是引用数据类型
    if(typeof obj !== 'object' || obj === null) return obj

    //如果是日期类型或正则类型,直接new返回
    if(obj instanceof Date){
        return new Date(obj)
    }
    if(obj instanceof RegExp){
        return new RegExp(obj)
    }

    //使用weakmap,解决循环引用的问题
    //如果当前key已经被存储进hashmap中
    if(hash.has(obj)){
        return hash.get(obj)
    }

    let newObj=obj.constructor()
    //将对象存入缓存0
    hash.set(obj,newObj)
    //使用Reflect.ownKeys,针对Symbol属性和不可枚举属性
    for(let key of Reflect.ownKeys(obj)){
        if(obj.hasOwnProperty(key)){
            //检查属性是否是自身属性
            //否则访问obj[key]时,会沿着原型链一直查找
            newObj[key]=deepClone(obj[key],hash)
        }
    }
    return newObj
}

实现call/apply

// 实现call/apply

//挂载到 Function.prototype上
Function.prototype.myCall = function(val,...args) {
    //判断this指向,需要指向function
    //谁调用这个方法,this就指向谁
    if(typeof this !== 'function') {
        throw new TypeError('error')
    }   

    //获取程序执行上下文
    let context=val || window

    let key=Symbol('fn')

    // 将当前函数绑定到 context 上
    context[key] = this;

    // 调用函数并获取结果
    const result = context[key](...args);

    // 删除临时属性
    delete context[key];

    return result;
}

实现bind

//手写实现bind
Function.prototype.myBind = function (fn, ...boundArgs) {
    // 判断 this 指向,需要指向 function
    if (typeof this !== 'function') {
        throw new Error('fn is not function');
    }

    // 创建一个新的函数
    const boundFunction = function (...args) {
        // 如果 new 被使用,则 this 指向新创建的对象
        const newThis = this instanceof boundFunction ? this : fn;

        // 合并预设参数和实际参数
        return this.apply(newThis, boundArgs.concat(args));
    };

    // 复制原函数的原型
    const self = this;
    Object.setPrototypeOf(boundFunction, self.prototype);

    return boundFunction;
};

实现防抖

// 防抖:n秒后执行事件,若n秒内再次触发,则重新计时

function debounce(fn,delay){
    //设置定时器
    let timer = null;
    return function(){
        //如果还存在定时器,清除定时器后重新计时
        if(timer) clearTimeout(timer);
        timer=setTimeout(fn,delay)
    }
}

实现节流

// 实现节流:n秒内只允许一次,若n秒内反复触发,只有一次生效
function throttle(fn,delay){
    //设置开关,是否允许执行
    let valid=true;
    return function(){
        if(!valid) return;
        //一定延迟后执行
        setTimeout(()=>{
            fn();
            valid=true;
        },delay)
    }
}

实现promise

//自定义promise

class MyPromise{
    //定义三种状态
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';

    //定义构造函数
    constructor(fn){
        //默认等待状态
        this.status=MyPromise.PENDING;
        //默认结果
        this.result=null

        try{
            fn(this.resolve.bind(this),this.reject.bind(this))
        }catch(err){
            //捕获异常
            this.reject(err)
        }
    }

    //定义resolve方法
    resolve(result){
        if(this.status===MyPromise.PENDING){
            this.status=MyPromise.FULFILLED
            this.result=result
        }
    }

    //定义reject方法
    reject(result){
        if(this.status===MyPromise.PENDING){
            this.status=MyPromise.REJECTED
            this.result=result
        }
    }

    //定义then方法
    then(resolve,reject){
        //判断是否为空函数
        resolve=typeof resolve==='function'?resolve:()=>{}
        reject=typeof reject==='function'?reject:()=>{}

        //根据当前status返回不同结果
        if(this.status===MyPromise.FULFILLED){
            setTimeout(()=>{resolve(this.result)})
        }

        if(this.status===MyPromise.REJECTED){
            setTimeout(()=>{reject(this.result)})
        }
    }
}

实现函数柯里化

//实现函数柯里化
  let currying = function (fn) {
    // args保存需要复用的参数
    let reuseArgs = Array.prototype.slice.call(arguments, 1)
    // 定义一个保存复用参数和自身参数的数组
    let args = reuseArgs
    return function () {
      // arguments是自身参数
      if (arguments.length === 0) {
        //最终执行
        return fn.apply(null, args)
      } else {
        [].push.apply(args, arguments)
      }
    }
  }

实现无限级目录树

function renderMenu(menuItems,parentElement){
    //获取页面上的ul元素
    const ul=document.createElement("ul");
    //遍历menuItems数组,生成li元素
    menuItems.forEach(item=>{
        const li=document.createElement("li");
        li.textContent=item.name;
        if(item.children && item.children.length>0){
            renderMenu(item.children,li);
        }
        ul.appendChild(li);
    })
    if(parentElement){
        parentElement.appendChild(ul);
    }
}

实现快速排序

//实现快速排序

function quickSort(arr){
    //递归中止条件
    if(arr.length<=1) return arr

    //定义基准值,可任取,这里取数组第一项
    let base=arr[0]
    //基准值左侧和右侧的数组
    let leftArr=[]
    let rightArr=[]

    //遍历数组,根据值的大小分别放入对应的数组
    for(let i=1;i<arr.length;i++){
        if(arr[i]<base) leftArr.push(arr[i])
        else rightArr.push(arr[i])
    }

    //合并数组
    let resArr=quickSort(leftArr).concat(base,quickSort(rightArr))

    return resArr
}

实现桶排序

function bucketSort(arr,bucketNum) {
    let length = arr.length;
    if (length <= 1) return arr;

    // 找到当前的最大值与最小值
    let max = Math.max(...arr);
    let min = Math.min(...arr);

    //对空桶赋值
    let buckets = Array.from({ length: bucketNum }, () => []);

    // 计算每个桶的大小
    let range = (max - min) / bucketNum+1;

    // 分配数据到桶中
    for (let i = 0; i < length; i++) {
        let index = Math.floor((arr[i] - min) / range);
        buckets[index].push(arr[i]);
    }

    console.log(buckets)

    // 每个桶内部进行排序
    // 合并桶元素
    let resultArr = [];
    for (let i = 0; i < bucketNum; i++) {
        buckets[i].sort((a, b) => a - b);
        resultArr = resultArr.concat(buckets[i]);
    }

    return resultArr;
}

实现计数排序

//计数排序
function countSort(arr){
    let length=arr.length
    let max=Math.max(...arr)
    let min =Math.min(...arr)

    //创建计数数组
    let countArr=new Array(max-min+1).fill(0)

    //遍历数组,统计每个数字出现的次数
    for(let i=0;i<length;i++){
        countArr[arr[i]-min]++
    }
    //遍历计数数组,根据次数生成结果数组
    let resultArr=[]
    for(let i=0;i<countArr.length;i++){
        for(let j=0;j<countArr[i];j++){
            resultArr.push(i+min)
        }
    }
    return resultArr
}

实现Promise.all()

如果参数中传入的不是promise对象,会隐式转换为promise对象并视为fuilled状态;如果某个promise不resolve也不reject,promise.all会永久等待

Promise.myAll = function (args) {
    if(!Array.isArray(args)){
        throw new Error('args is not a array')
    }
    // 成功和失败的结果
    let res, rej;
    const promise = new Promise((resolve, reject) => {
        res = resolve;
        rej = reject;
    });

    // 记录接收的promise对象的数量
    let count = 0;
    // 每个promise返回的结果数组
    const result = [];
    // 需要完成的Promise总数
    const total = args.length;

    // 如果args为空,直接返回成功
    if (total === 0){
        res([])
        return promise
    }

    args.forEach((i, index) => {
        Promise.resolve(i).then(
            data => {
                result[index] = data;
                count++;
                // 所有Promise都完成时,调用resolve
                if (count === total) {
                    res(result);
                }
            },
            error => {
                // 任一Promise失败,则立即拒绝整个Promise.myAll
                rej(error);
            }
        );
    });

    return promise;
};

手写实现Promise.any

Promise.myAny=function(args){
    if(!Array.isArray(args)){
        throw new Error('args is not a array')
    }
    let res,rej

    //为了方便的在方法内部访问resolve和reject
    const promise=new Promise((resolve,reject)=>{
        res=resolve
        rej=reject
    })

    //存储结果
    let result=[]
    let count=0
    let total=args.length || 0
    if(total==0) rej('no promise')
    //遍历每个传入的promise对象
    args.forEach((item, index) => {
        Promise.resolve(item).then(data => {
            res(data); // 第一个解决的 promise 就解决整个 promise
        }, err => {
            count++
            if(count==total) rej('all promise reject')
        });
    });
    return promise
}

手写实现promise.race

Promise.myRace=function(args){
    if(!Array.isArray(args)){
        throw new Error('args is not a array')
    }
    let res,rej

    //为了方便的在方法内部访问resolve和reject
    const promise=new Promise((resolve,reject)=>{
        res=resolve
        rej=reject
    })

    //存储结果
    let result=[]
    let count=0
    let total=args.length 
    if(total==0) rej('no promise')
    //遍历每个传入的promise对象
    args.forEach((item, index) => {
        Promise.resolve(item).then(data => {
            res(data); // 第一个解决的 promise 就解决整个 promise
        }, err => {
            rej(err); // 第一个拒绝的 promise 就拒绝整个 promise
        });
    });
    return promise
}

手写实现async/await

搞清楚async await本质是使用Generator实现的,async返回的是promise对象

function myAsync(generatorFunc) {
  // 创建生成器函数实例
  const gen = generatorFunc.apply(this, arguments);

  // 返回一个 Promise 对象
  return new Promise((resolve, reject) => {
    // 创建 step 函数
    function step(val) {
      let result;
      try {
        result = gen.next(val);
      } catch (err) {
        return reject(err);
      }

      if (result.done) {
        return resolve(result.value);
      } else {
        //将value转换为promise对象,继续链式调用
        return Promise.resolve(result.value).then(
          val => step(val),
          err => step(err)
        );
      }
    }

    // 初始调用 step 函数
    step();
  });
}

实现数组扁平化

function flatten(arr){
    if(!Array.isArray(arr)){
        throw new Error('arr is not a array')
    }
    //递归实现,注意初始化pre为[]
    return arr.reduce((pre,cur)=>{
        return pre.concat(Array.isArray(cur)?flatten(cur):cur)
    },[])
}

实现对象扁平化

function flatten(object){
    var result={}
    //pre是前缀
    function format(obj,pre){
        for(let key in obj){
            if(typeof obj[key]=='object'){
                if(pre){
                    format(obj[key],pre+'.'+key)
                }else{
                    format(pbj[key],key)
                }
            }else{
                if(pre){
                    result[pre+'.'+key]=obj[key]
                }else{
                    result[key]=obj[key]
                }
            }
        }
    }
    format(object,null)
    return result
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值