首页 > 编程语言 >前端面试手撕算法大全

前端面试手撕算法大全

时间:2024-08-09 14:26:36浏览次数:9  
标签:function const 对象 return 面试 算法 Promise obj 大全

写在前面

文末有我在前端面试多年的经验文章,分享给大家!!!

防抖

// 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

// 防抖(debounce)函数是指在一定时间内,事件被触发多次,只执行最后一次。这在处理诸如输入框实时搜索等场景时非常有用,避免了频繁触发事件导致性能问题。

const debounce  =  (Fn, wait) => {
  let timer;
  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      Fn.apply(this,args)
    },wait)
  }
}

// 使用示例
const handleSearch = event => {
  console.log("search:", event.target.value);
};

const debouncedSearch = debounce(handleSearch, 300);

节流

// 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
// 适用场景:
// 拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
// 缩放场景:监控浏览器resize
// 动画场景:避免短时间内多次触发动画引起性能问题

const throttle = (Fn, wait = 500) => {
  let lastTime = 0
  return function (...args) {
    let now = +new Date()
    if (now - lastTime > wait) {
      lastTime = now
      Fn.apply(this,args)
    }
  }
}

const fn = throttle(() => {
  console.log(3);
}, 3000)

setInterval(fn, 2000)

Instanceof

function myInstanceof(left, right) {
  // 获取 right 的 prototype
  let prototype = right.prototype;
  
  // 获取 left 的 __proto__
  let proto = left.__proto__;
  
  // 遍历原型链,直到找到相同的原型或到达 null
  while (proto !== null) {
    if (proto === prototype) {
      return true;
    }
    proto = proto.__proto__;
  }
  
  // 如果没有找到相同的原型,则返回 false
  return false;
}

// 示例
class Animal {}
class Dog extends Animal {}

const dog = new Dog();

console.log(myInstanceof(dog, Dog)); // true
console.log(myInstanceof(dog, Animal)); // true
console.log(myInstanceof(dog, Object)); // true
console.log(myInstanceof(dog, String)); // false

手写new

function myNew(constructor, ...args) {  
    // 1. 创建一个新的空对象  
    let obj = {};  
  
    // 2. 将这个新对象的 __proto__ 属性链接到构造函数的 prototype 对象  
    obj.__proto__ = constructor.prototype;  
  
    // 3. 将这个新对象作为 this 的上下文  
    // 使用 Function.prototype.call 或 Function.prototype.apply 调用构造函数  
    // 并将参数传递给它  
    let result = constructor.apply(obj, args);  
  
    // 4. 如果构造函数返回了一个对象,那么就返回这个对象;如果没有返回对象,那么就返回新创建的对象 obj。
    return result instanceof Object ? result : obj;  
}  
  
// 使用示例  
function Person(name, age) {  
    this.name = name;  
    this.age = age;  
}  
  
Person.prototype.greet = function() {  
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);  
};  
  
let person = myNew(Person, "Alice", 30);  
person.greet();  // 输出: Hello, my name is Alice and I'm 30 years old.

数组去重

var array = [1, 2, 1, 1, "1"];

// Array.from去重
function uniques(array) {
  return Array.from(new Set(array));
}

// 简化
function SampleUniques(array) {
  return [...new Set(array)];
}
console.log(uniques(array));

// 也可以使用es5中的indexOf方法
function es5Uniques(array) {
  let res = array.filter(function (item, index, array) {
    return array.indexOf(item) === index;
  });
  return res;
}

console.log("es5去重");
console.log(es5Uniques(array));


const array = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [];
const map = new Map();

for (const item of array) {
    if (!map.has(item)) {
        map.set(item, true);
        uniqueArray.push(item);
    }
}

console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]

深拷贝

深拷贝(Deep Copy)是指完全复制一个对象,包括其所有嵌套的子对象。深拷贝与浅拷贝(Shallow Copy)不同,浅拷贝只复制对象的第一层属性,对于嵌套的对象仍然是引用。

在JavaScript中,有多种方法可以实现深拷贝。以下是几种常见的方法:

方法一:使用 JSON.parse 和 JSON.stringify

这是最简单的方法,但它有一些限制,比如不能处理函数、undefinedSymbol、循环引用等。

const obj = {a: 1,b: {c: 2,d: 3
    }
};

const deepCopy = JSON.parse(JSON.stringify(obj));
console.log(deepCopy);  // 输出: { a: 1, b: { c: 2, d: 3 } } 

function deepClone(obj) {
  if(typeof obj != 'object' && obj != null){
    return obj;
  }

  let result;

  if(obj instanceof Array){
    result = []
  }else{
    result = {};
  }

  for(let key in obj){
    if(obj.hasOwnProperty(key)){
      result[key] = deepClone(obj[key]);
    }
  }

  return result;

}

拍平数组

const arr = [1, 2, [3, 4, [5, 6]]];

function flatten(arr) {
  while (arr.some((item) => Array.isArray(item))) {
    arr = arr.flatMap((item) => item);
  }
  return arr;
}

const flattened = flatten(arr); // [1, 2, 3, 4, 5, 6]


function flattenArray(arr) {
    let result = [];
    arr.forEach(item => {
        if (Array.isArray(item)) {
            result = result.concat(flattenArray(item));
        } else {
            result.push(item);
        }
    });
    return result;
}

// 测试 flattenArray
const array = [1, [2, [3, [4, 5]]]];
const flatArray = flattenArray(array);
console.log(flatArray); // 输出: [1, 2, 3, 4, 5] 

手写promiseAll

Promise.all 是一个非常有用的工具,它接受一个包含多个 Promise 的可迭代对象(通常是数组),并返回一个新的 Promise。这个新的 Promise 在所有输入的 Promise 都成功时解析,并且解析值是一个包含所有输入 Promise 解析值的数组。如果任何一个输入的 Promise 拒绝(reject),则返回的 Promise 也会立即拒绝,并且拒绝原因是第一个拒绝的 Promise 的原因。

function promiseAll(promises){
  if(!Array.isArray(promises)){
    throw new TypeError("promises must be an array")
  }

  return new Promise(function(resolve, reject){
    // 数组长度
    let promiseNum = promises.length;
    // 成功的数量
    let resolveCount  = 0;
    // 成功的值的数组
    let resolveValues = new Array(promiseNum);
    // 先遍历
    for(let i=0; i<promiseNum; i++){
      // 为什么不直接 promise[i].then, 因为promise[i]可能不是一个promise
      Promise.resolve(promises[i]).then(function(value){
        resolveValues[i] = value;
        resolveCount++;
        if(resolveCount == promiseNum){
          return resolve(resolveValues)
        }
      },
      function(err){
        return reject(err);
      }
      )
    }
  })
}

手写promiseRace

Promise.race 是一个非常有用的工具,它接受一个包含多个 Promise 的可迭代对象(通常是数组),并返回一个新的 Promise。这个新的 Promise 在第一个输入的 Promise 解析或拒绝时解析或拒绝,并且解析值或拒绝原因是第一个完成的 Promise 的值或原因。

解释

  1. 输入验证:首先,我们检查传入的参数是否是一个数组。如果不是数组,我们立即拒绝返回的 Promise,并抛出一个 TypeError
  2. 遍历输入的 Promise:我们使用 for...of 遍历输入的 Promise 数组。对于每个 Promise,我们使用 Promise.resolve 将其转换为一个标准的 Promise(以防输入的某个值本身不是一个 Promise)。
  3. 处理每个 Promise
    1. 如果 Promise 成功解析,我们立即调用 resolve,并传入解析值。
    2. 如果 Promise 拒绝,我们立即调用 reject,并传入拒绝原因。

注意事项

  • Promise.resolve(promise) 确保即使传入的值不是一个 Promise,也会被转换为一个 Promise。
  • 一旦有任何一个 Promise 解析或拒绝,我们立即解析或拒绝返回的 Promise,并传入相应的值或原因。

这个实现基本上覆盖了 Promise.race 的核心功能,可以处理大多数常见的使用场景。

function promiseRace(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises)) {
            return reject(new TypeError('Argument must be an array'));
        }

        for (const promise of promises) {
            Promise.resolve(promise)
                .then(resolve)
                .catch(reject);
        }
    });
}

// 测试 promiseRace
const promise1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'two');
});

promiseRace([promise1, promise2])
    .then(value => {
        console.log(value); // 输出: 'two'
    })
    .catch(error => {
        console.error(error);
    });

快速排序

function QuickSort(arr) {
  const n = arr.length;
  if (n <= 1) return arr;
  let pivot = arr[0];

  let left = [];
  let right = [];

  for (let i = 1; i < n; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  // return QuickSort(left).concat([pivot], QuickSort(right));
  return [...QuickSort(left), pivot, ...QuickSort(right)];
}
let list = [4, 6, 8, 5, 9, 1, 2, 3, 2];

let sortArr = QuickSort(list);
console.log("快速排序", sortArr);

在JavaScript中,applycallbind是Function对象的三个方法,用于改变函数的执行上下文(即this的指向)。我们可以使用ES6的特性来手动实现这些方法。以下是它们的实现:

实现 apply

分析:如何在函数执行时绑定this

  • var obj = {x:100,fn() { this.x }}
  • 执行obj.fn() ,此时fn内部的this就指向了obj
  • 可借此来实现函数绑定this

apply方法调用一个函数,并指定this和参数数组。

Function.prototype.myApply = function(context, args) {
    context = context || globalThis; // globalThis 是 ES2020 引入的全局对象
    const fnSymbol = Symbol(); // 使用 Symbol 防止属性冲突
    context[fnSymbol] = this; // this 指向当前函数
    const result = context[fnSymbol](...args); // 展开参数数组并调用函数
    delete context[fnSymbol]; // 删除临时属性
    return result;
};

// 测试 myApply
function greet(greeting, punctuation) {
    return `${greeting}, ${this.name}${punctuation}`;
}

const person = { name: 'Alice' };
console.log(greet.myApply(person, ['Hello', '!'])); // 输出: "Hello, Alice!" 

实现 call

call方法与apply类似,但它接受的是参数列表而不是参数数组。

Function.prototype.myCall = function(context, ...args) {
    context = context || globalThis;
    const fnSymbol = Symbol();
    context[fnSymbol] = this;
    const result = context[fnSymbol](...args);
    delete context[fnSymbol];
    return result;
};

// 测试 myCall
console.log(greet.myCall(person, 'Hi', '?')); // 输出: "Hi, Alice?" 

实现 bind

bind方法返回一个新的函数,该函数在调用时将this设置为提供的值,并在调用新函数时预设一定数量的参数。

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

// 测试 myBind
const boundGreet = greet.myBind(person, 'Hey');
console.log(boundGreet('!!!')); // 输出: "Hey, Alice!!!" 

好的,让我们一步一步地实现Object.create()Object.assign(),并在代码中添加注释进行解释。

实现 Object.create()

首先,我们来实现 Object.create()。这个方法需要创建一个新对象,并将其原型设置为指定的 proto,同时可以选择性地定义一些属性。

Object.myCreate = function (proto, defineProperties) {
    // 检查 proto 是否是对象或函数
    if ((typeof proto === 'object' && proto !== null) || typeof proto === 'function') {
        // 创建一个空对象
        let obj = {};

        // 设置新对象的原型为 proto
        Object.setPrototypeOf(obj, proto);

        // 如果 defineProperties 存在,使用 Object.defineProperties 定义属性
        if (defineProperties !== undefined) {
            Object.defineProperties(obj, defineProperties);
        }

        // 返回新对象
        return obj;
    } else {
        // 如果 proto 不是对象或函数,抛出类型错误
        throw new TypeError('类型错误');
    }
}

类型检查:首先检查 proto 是否是对象或函数。如果不是,抛出类型错误。

创建空对象:使用字面量 {} 创建一个空对象。

设置原型:使用 Object.setPrototypeOf(obj, proto) 将新对象的原型设置为 proto

定义属性:如果 defineProperties 存在,使用 Object.defineProperties(obj, defineProperties) 定义新对象的属性。

返回新对象:返回新创建的对象。

实现 Object.assign()

接下来,我们来实现 Object.assign()。这个方法需要将一个或多个源对象的可枚举属性复制到目标对象中。

Object.myAssign = function (target, ...sources) {
    // 检查 target 是否是对象或函数
    if (target == null) {
        throw new TypeError('Cannot convert undefined or null to object');
    }

    // 将 target 转换为对象
    let to = Object(target);

    // 遍历每个源对象
    for (let i = 0; i < sources.length; i++) {
        let nextSource = sources[i];

        // 如果源对象不为 null 或 undefined
        if (nextSource != null) {
            // 遍历源对象的所有可枚举属性
            for (let key in nextSource) {
                // 只复制源对象自身的属性
                if (Object.prototype.hasOwnProperty.call(nextSource, key)) {
                    to[key] = nextSource[key];
                }
            }
        }
    }

    // 返回目标对象
    return to;
}

类型检查:首先检查 target 是否为 null 或 undefined。如果是,抛出类型错误。
转换为对象:使用 Object(target) 将 target 转换为对象。
遍历源对象:使用 for 循环遍历每个源对象。
检查源对象:如果源对象不为 null 或 undefined,继续处理。
遍历属性:使用 for...in 循环遍历源对象的所有可枚举属性。
复制属性:使用 Object.prototype.hasOwnProperty.call(nextSource, key) 检查属性是否为源对象自身的属性。如果是,将其复制到目标对象 to 中。
返回目标对象:返回目标对象 to

接下来给大家推荐一篇我在前端面试多年的经验文章,希望大家看完以后都可以领取到心仪的offer哦!

文章:《聊聊前端面试那些事儿》

标签:function,const,对象,return,面试,算法,Promise,obj,大全
From: https://blog.csdn.net/2401_84489283/article/details/141021124

相关文章

  • Delphi编程常用快捷键大全
    快捷键对于提高编程速度有着很重要的影响,本文汇总整理了Delphi常用的快捷键,供大家参考之用:Ctrl+PageUp将光标移至本屏的第一行,屏幕不滚动。Ctrl+PageDown将光标移至本屏的最后一行,屏幕不滚动。Ctrl+↓向下滚动屏幕,光标跟随滚动不出本屏。Ctrl+↑向上滚动屏幕,光标跟随滚动不出本屏......
  • 室内定位导航技术:蓝牙信号强度(RSSI)与三角定位算法应用
    在数字化时代,位置服务已成为连接物理世界与数字世界的桥梁。在室内环境中,由于GPS信号受建筑物遮挡而失效,传统的室外定位技术难以满足需求,无法精准指引我们在商场、机场、医院等庞大而复杂的建筑内部寻路。室内定位导航技术不仅解决了室内空间定位的盲区问题,更是极大的提升了用......
  • 11.面试题——消息队列RabbitMQ
    1.RabbitMQ是什么?特点是什么?RabbitMQ是一种开源的消息队列中间件,用于在应用程序之间进行可靠的消息传递。它实现了AMQP(AdvancedMessageQueuingProtocol)协议,提供了强大的消息处理能力。RabbitMQ的主要特点包括:可靠性:RabbitMQ使用可靠的消息传递机制,确保消息能够安全地传......
  • 【面试高频,必知必会】OpenGL渲染流程
    ......
  • 「代码随想录算法训练营」第三十三天 | 动态规划 part6
    322.零钱兑换题目链接:https://leetcode.cn/problems/coin-change/文章讲解:https://programmercarl.com/0322.零钱兑换.html题目难度:中等视频讲解:https://www.bilibili.com/video/BV14K411R7yv/题目状态:略微有点思路,但还是有点转不过来。思路:这次是找最小的钱币组合,因此......
  • OpenCV 分水岭算法
    使用分水岭算法进行图像分割(一)获取灰度图像,二值化图像,进行形态学操作,消除噪点defwatershed_demo(image):blur=cv.pyrMeanShiftFiltering(image,10,100)gray=cv.cvtColor(blur,cv.COLOR_BGR2GRAY)#获取灰度图像ret,binary=cv.threshold(gray,0,2......
  • Python面试宝典第30题:找出第K大元素
    题目        给定一个整数数组nums,请找出数组中第K大的数,保证答案存在。其中,1<=K<=nums数组长度。        示例1:输入:nums=[3,2,1,5,6,4],K=2输出:5        示例2:输入:nums=[50,23,66,18,72],K=3输出:50快速选择算法......
  • 基于paddlehub 未戴口罩检测算法
    一、简介以前大夏天戴着口罩别人觉得你不正常,现在上街不戴口罩你不正常。本文要讲的未戴口罩算法是基于paddlehub提供的模型,paddlehub是百度飞浆(PaddlePaddle)下的深度学习模型开发工具。PaddleHub旨在为开发者提供丰富的、高质量的、直接可用的预训练模,先看实际效果图......
  • fpga图像处理实战-RGB转YCBCR(YUV)算法
    YCBCR        YCbCr是一种用于数字图像和视频的颜色空间。它将颜色分解为亮度(Y)和两个色度分量(Cb和Cr)。这种分解使得视频压缩和传输更加高效,因为人眼对亮度变化更敏感,而对色度变化较不敏感。        亮度(Y):亮度表示图像的灰度级别或光强度。它是RGB颜色空间中......
  • 哪种编程语言更适合学习数据结构和算法:C++、Java 还是 Python?
    作为一名工程专业的学生,​​我正在尝试决定使用哪种编程语言来学习数据结构和算法(DSA)。我正在考虑C++,它提供高性能和强大的标准模板库,但对于初学者来说可能很复杂。Java具有强大的语法和内置集合,使DSA概念更容易掌握,尽管我不确定它与C++相比的性能。Python以其简单性和......