基础练习一

48 阅读3分钟

1. 数组去重

功能:实现一个方法,去除数组中重复元素

原理:利用filter 和 indexOf通过判断元素第一次出现的索引值是否和当前索引来去重;

function uniqueArr(arr) {
    let newArr = [];
    newArr = arr.fliter((item, index)=> arr.indexOf(item) === index)
    return newArr;
}

原理:set 是es6中的数据结构,自动去重,通过[...new Set(arr)]可以快速去重

function uniqueArr(arr) {
    retun [... new Set(arr)];
}

还有其他方法就不一一列举

2.反转字符串

功能:实现一个方法,反转一个字符串(如:hello -》 olleh)

原理:利用split将字符串转为数组,然后数组利用reverse方法进行反转,然后再通过join合并回字符串;

function reverseString (string){
    let newString = '';
    newString = string.split('').reverse().join('')
    return newString;
}

原理:for循环从尾部开始遍历,然后拼接成新字符串

function reverseString (string){
    let reverseSring = '';
    for(let i=string.length-1; i>=0; i--){
        reverseString + = string[i];
    }
    return reverseSring;
}

3.深拷贝对象

功能:实现一个对象的深拷贝(处理嵌套对象和数组)

原理:

  • 递归遍历对象的每个属性,对基本类型直接返回,引用类型继续递归。
  • 使用 WeakMap 记录已拷贝对象,解决循环引用问题。
function deepClone(obj, map = new WeakMap()) {
  if (typeof obj !== 'object' || obj === null) return obj;
  if (map.has(obj)) return map.get(obj); // 处理循环引用
  
  const clone = Array.isArray(obj) ? [] : {};
  map.set(obj, clone);
  
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], map);
    }
  }
  return clone;
}

4.判断回文字符串

问题:判断一个字符串是否是回文(如 "A man, a plan, a canal: Panama")。

function isPalindrome(s) {
  const clean = s.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
  return clean === clean.split('').reverse().join('');
}
// 或双指针优化
function isPalindrome(s) {
  const clean = s.replace(/[^a-z0-9]/gi, '').toLowerCase();
  let left = 0, right = clean.length - 1;
  while (left < right) {
    if (clean[left] !== clean[right]) return false;
    left++;
    right--;
  }
  return true;
}

原理

  • 先清理非字母数字字符并转为小写,再比较反转后的字符串。
  • 双指针法减少时间复杂度到 O(n/2)。

5.找出数组中的最大差值

功能:给定一个数组,计算最大差值

原理:遍历数组,记录当前最小值,并计算当前数组最大值和最小值的差值,然后再更新最大差值;

function maxProfit (prices){
    let minPrice = 0;
    let maxProfit = 0;
    for(let price of prices){
        minPrice = Math.min(prices, price);
        maxProfit = Math.max(maxProfit, price - minPrice)
    }
    retrun maxProfit;
}

6. 合并两个有序数组

问题:将两个有序数组合并为一个有序数组(假设第一个数组有足够空间)。
答案

function merge(nums1, m, nums2, n) {
  let i = m - 1, j = n - 1, k = m + n - 1;
  while (j >= 0) {
    nums1[k--] = (i >= 0 && nums1[i] > nums2[j]) ? nums1[i--] : nums2[j--];
  }
}

原理

  • 从后向前遍历,避免覆盖 nums1 的元素,比较两数组当前最大值,填入末尾。

7. 事件循环输出顺序

问题:以下代码的输出顺序是什么?

console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');

答案1 → 4 → 3 → 2
原理

  • 同步代码(14)最先执行。
  • 微任务(Promise)优先于宏任务(setTimeout)执行。

8. 实现防抖(Debounce)

问题:手写防抖函数,限制高频触发的事件。
答案

function debounce(fn, delay, immediate = false) {
  let timer;
  return function(...args) {
    if (immediate && !timer) fn.apply(this, args);
    clearTimeout(timer);
    timer = setTimeout(() => {
      if (!immediate) fn.apply(this, args);
      timer = null;
    }, delay);
  };
}

原理

  • 每次触发时清除之前的定时器,重新计时,直到停止触发后执行最后一次。

9. 有效的括号

问题:判断字符串中的括号是否有效(如 "()[]{}" 有效,"(]" 无效)。
答案

function isValid(s) {
  const stack = [];
  const map = { '(': ')', '[': ']', '{': '}' };
  for (let char of s) {
    if (map[char]) stack.push(char);
    else if (map[stack.pop()] !== char) return false;
  }
  return stack.length === 0;
}

原理

  • 用栈结构处理左括号,遇到右括号时检查栈顶是否匹配。

10.手写Promise.all

功能:实现Promise.all的功能

原理:遍历Promise数组,统计完成数量,全部成功则返回结果数据,任一失败则立即拒绝;

function promiseAll(promises){
    return new Promise(resole,reject=>{
        const results = [];
        let count = 0;
        promises.forEach((p,i)=>{
            Promise.resolve(p).then((res)=>{
                   results[i] = res;
                   count ++;
                   if(count===promises.length){
                       resole(esults)
                   }
            }).catch(reject)
        })
    })
}