伟大的日常

一、永恒的经典

const arr = [1, 8, 2, 1, 0, 6];
const N = arr.length;
// 冒泡
for (let i = 0; i < N - 1; i++) {
  for (let j = 0; j < N - 1 - i; j++) {
    if (arr[j] > arr[j + 1]) {
      [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
    }
  }
}
// 插入
for (let i = 1; i < N; i++) {
  let j;
  let key = arr[i];
  for (j = i - 1; j >= 0 && arr[j] > key; j--) {
    arr[j + 1] = arr[j];
  }
  arr[j + 1] = key;
}
// 选择
for (let i = 0; i < N - 1; i++) {
  let minIndex = i;
  for (let j = i + 1; j < N; j++) {
    if (arr[j] < arr[minIndex]) {
      minIndex = j;
    }
  }
  if (minIndex != i) {
    [arr[minIndex], arr[i]] = [arr[i], arr[minIndex]];
  }
}
// 归并
function mergeSort(arr) {
  if (arr.length <= 1) return arr;
  const mid = Math.floor(arr.length / 2);
  const left = mergeSort(arr.slice(0, mid));
  const right = mergeSort(arr.slice(mid));
  return merge(left, right);
}
function merge(left, right) {
  let result = [];
  let i = 0;
  let j = 0;
  while (i < left.length && j < right.length) {
    if (left[i] < right[j]) {
      result.push(left[i++]);
    } else {
      result.push(right[j++]);
    }
  }
  return result.concat(left.slice(i)).concat(right.slice(j));
}
// 快速
function quickSort(arr, low = 0, high = arr.length - 1) {
  if (low < high) {
    // 获取分区点
    const pivotIndex = partition(arr, low, high);
    // 递归排序左半部分
    quickSort(arr, low, pivotIndex - 1);
    // 递归排序右半部分
    quickSort(arr, pivotIndex + 1, high);
  }
  return arr;
}
function partition(arr, low, high) {
  // 选择最右侧元素作为基准
  const pivot = arr[high];
  let i = low - 1;
  // 将小于基准的元素移到左侧
  for (let j = low; j < high; j++) {
    if (arr[j] <= pivot) {
      i++;
      [arr[i], arr[j]] = [arr[j], arr[i]]; // 交换元素
    }
  }
  // 将基准放到正确位置
  [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
  return i + 1;
}

// 测试
const arr = [38, 27, 43, 3, 9, 82, 10];
console.log(quickSort(arr)); // [3, 9, 10, 27, 38, 43, 82]
// 堆排
function heapSort(arr) {
  const n = arr.length;
  // 构建最大堆(从最后一个非叶子节点开始)
  for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
    heapify(arr, n, i);
  }
  // 逐个提取元素
  for (let i = n - 1; i > 0; i--) {
    // 将堆顶元素与末尾元素交换
    [arr[0], arr[i]] = [arr[i], arr[0]];
    // 对剩余堆进行调整
    heapify(arr, i, 0);
  }
  return arr;
}
function heapify(arr, n, i) {
  let largest = i; // 初始化最大值为根节点
  const left = 2 * i + 1; // 左子节点
  const right = 2 * i + 2; // 右子节点
  // 如果左子节点大于根节点
  if (left < n && arr[left] > arr[largest]) {
    largest = left;
  }
  // 如果右子节点大于当前最大值
  if (right < n && arr[right] > arr[largest]) {
    largest = right;
  }
  // 如果最大值不是根节点
  if (largest !== i) {
    [arr[i], arr[largest]] = [arr[largest], arr[i]]; // 交换元素
    // 递归调整受影响的子堆
    heapify(arr, n, largest);
  }
}

// 测试
const arr = [38, 27, 43, 3, 9, 82, 10];
console.log(heapSort(arr)); // [3, 9, 10, 27, 38, 43, 82]

二、节流、防抖与柯里化

// 节流
function throttle(fn, interval) {
  let callTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - callTime >= interval) {
      fn.apply(this, args);
      callTime = now;
    }
  };
}
//防抖
function debounce(fn, delay) {
  let timer = 0;
  return function (...args) {
    timer && clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}
// 柯里化函数
function curry(fn) {
  const arity = fn.length; // 原函数的参数个数
  function curried(...args) {
    if (args.length >= arity) {
      return fn(...args); // 参数足够,直接调用原函数
    } else {
      // 参数不足,返回一个新函数继续收集
      return function (...moreArgs) {
        return curried(...args, ...moreArgs); // 关键:合并旧新参数并递归
      };
    }
  }
  return curried;
}
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args); // 参数足够时执行
    } else {
      return function (...moreArgs) {
        return curried.apply(this, args.concat(moreArgs)); // 闭包累积参数
      };
    }
  };
}

const add = curry((a, b, c) => a + b + c);
console.log(add(1)(2)(3)); // 6
console.log(add(1, 2)(3)); // 6

三、普通函数与箭头函数的this

function outer() {
  if (true) {
    const arrowFunc = () => {
      console.log("箭头~", this);
    };
    arrowFunc();
    const regularFunc = function () {
      console.log("普通~", this);
    };
    regularFunc();
  }
}
outer.call({
  name: "Charlie",
});
//{name:"Charlie"} Window{}
outer();
//Window{}  Window{}

const obj = {
  name: "Bob",
  regularMethod: function () {
    const arrowFunc = () => {
      console.log("箭头~", this);
    };
    arrowFunc();
    const regularFunc = function () {
      console.log("普通~", this);
    };
    regularFunc();
  },
};
obj.regularMethod();
//箭头 {name: "Bob"}
//普通 Window{}

四、易混淆知识点

Array Array.of Array.from
splice slice substring substr

五、经典笔试题

1、发布订阅模式
// 发布订阅模式
class EventEmitter {
  constructor() {
    this.events = [];
  }
  on(event, cb) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(cb);
  }
  emit(event, ...args) {
    if (this.events[event]) {
      this.events[event].forEach((cb) => {
        cb(...args);
      });
    }
  }
  off(event, cb) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(
        (callback) => callback !== cb,
      );
    }
  }
}

const emitter = new EventEmitter();
emitter.on("msg", (msg) => {
  console.log(`hi,${msg}`);
});
emitter.emit("msg", "999");
2、并发控制
// 并发控制
async function runTasks(tasks, concurrency) {
  const results = [];
  const runningTasks = new Set(); // 使用 Set 提高删除效率

  for (let i = 0; i < tasks.length; i++) {
    // 1. 执行当前任务
    const taskPromise = tasks[i]().then((res) => {
      // 2. 任务完成后从运行集合中移除
      runningTasks.delete(taskPromise);
      // 3. 按原始顺序保存结果
      results[i] = res;
    });

    // 4. 将任务添加到运行集合
    runningTasks.add(taskPromise);

    // 5. 如果达到并发限制,等待任意任务完成
    if (runningTasks.size >= concurrency) {
      await Promise.race(runningTasks);
    }
  }

  // 6. 等待所有任务完成
  await Promise.all(runningTasks);

  return results;
}
const tasks = [
  () =>
    new Promise((resolve) =>
      setTimeout(() => {
        resolve("Task 1");
      }, 1000),
    ),
  () =>
    new Promise((resolve) =>
      setTimeout(() => {
        resolve("Task 2");
      }, 500),
    ),
  () =>
    new Promise((resolve) =>
      setTimeout(() => {
        resolve("Task 3");
      }, 2000),
    ),
];
runTasks(tasks, 2).then((res) => {
  console.log(res);
});
3、爬楼梯
function climbStairs(n) {
  if (n <= 2) return n;
  let prev1 = 1;
  let prev2 = 2;
  for (let i = 3; i <= n; i++) {
    const current = prev1 + prev2;
    prev1 = prev2;
    prev2 = current;
  }
  return prev2;
}
function climbStairs(n) {
  if (n < 3) return n;
  // return jump(n - 1) + jump(n - 2)
  let prev1 = 1,
    prev2 = 2;
  while (n-- > 2) {
    [prev1, prev2] = [prev2, prev1 + prev2];
  }
  return prev2;
}
4、有效回文串
//有效回文串
function canMakePalindrome(s) {
  let mismatch = 0;
  for (let i = 0, j = s.length - 1; i < j; i++, j--) {
    //if(s[i] !== s[j]) mismatch++;
    //if(mismatch > 2) return false;
    if (s[i] !== s[j] && ++mismatch > 2) return false;
  }
  return mismatch < 3;
}
5、深度克隆
/*
Object/Array/Date/RegExp/Map/Set/Function/TypedArray 与循环引用的 deepClone,并考虑原型链与不可枚举属性。
// 简单
// 循环引用
// Date
// RegExp
// Map
// Set
// Function
// ArrayBuffer
// TypedArray
// Object Array
// 不可枚举属性
*/
function deepClone(obj, hash = new WeakMap()) {
  // 处理基本类型和null/undefined
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  // 处理循环引用
  if (hash.has(obj)) {
    return hash.get(obj);
  }

  // 处理Date对象
  if (obj instanceof Date) {
    const clone = new Date(obj);
    hash.set(obj, clone);
    return clone;
  }

  // 处理RegExp对象
  if (obj instanceof RegExp) {
    const clone = new RegExp(obj);
    hash.set(obj, clone);
    return clone;
  }

  // 处理Map对象
  if (obj instanceof Map) {
    const clone = new Map();
    hash.set(obj, clone);
    obj.forEach((value, key) => {
      clone.set(deepClone(key, hash), deepClone(value, hash));
    });
    return clone;
  }

  // 处理Set对象
  if (obj instanceof Set) {
    const clone = new Set();
    hash.set(obj, clone);
    obj.forEach((value) => {
      clone.add(deepClone(value, hash));
    });
    return clone;
  }

  // 处理TypedArray
  if (ArrayBuffer.isView(obj)) {
    const clone = new obj.constructor(obj);
    hash.set(obj, clone);
    return clone;
  }

  // 处理ArrayBuffer
  if (obj instanceof ArrayBuffer) {
    const clone = obj.slice(0);
    hash.set(obj, clone);
    return clone;
  }

  // 处理Function(通常函数不需要克隆,直接返回原函数)
  if (typeof obj === "function") {
    // 如果需要真正克隆函数,可以使用eval或new Function,但通常不推荐
    return obj;
  }

  // 处理数组和普通对象
  const clone = Object.create(Object.getPrototypeOf(obj));
  hash.set(obj, clone);

  // 获取所有属性(包括不可枚举属性)
  const allKeys = Object.getOwnPropertyNames(obj).concat(
    Object.getOwnPropertySymbols(obj),
  );

  for (const key of allKeys) {
    // 使用描述符克隆属性,保持属性特性
    const descriptor = Object.getOwnPropertyDescriptor(obj, key);
    if (descriptor) {
      if (typeof descriptor.value === "object" && descriptor.value !== null) {
        descriptor.value = deepClone(descriptor.value, hash);
      }
      Object.defineProperty(clone, key, descriptor);
    }
  }

  return clone;
}
6、sp通配符匹配
/**
 * @param {string} s
 * @param {string} p
 * @return {boolean}
 */
/**
 * 动态规划,前提:递归 回溯
 * 动态规划<=穷举=>抽象树形结构+回溯
 */
/**
 * 状态dp[i][j]:表示s的前i个字符和p的前j个字符是否匹配
 * 状态转移方程:
 *  1.s[i]===p[j] or p[j]==='?'   dp[i][j]=dp[i-1][j-1]
 *  2.p[j]==='*'  那么 dp[i][j]=dp[i][j-1]||dp[i-1][j]
 *    dp[i][j-1] *代表空字符, ab ab*
 *    dp[i-1][j] *代表非空字符,abcd ab*
 * 初始化:
 *  1.dp[0][0] 什么都没有 true
 *  2.dp[0][j] 第一行,s为空,只要p开始为*才为true
 *  3.dp[i][0] 第一列,p为空,s不为空,始终为false
 */
var isMatch = function (s, p) {
  const m = s.length,
    n = p.length;

  // 状态定义:dp[i][j]表示s的前i个字符和p的前j个字符是否匹配
  //const dp = new Array(m + 1).fill(false).map(() => new Array(n + 1).fill(false))
  const dp = Array.from({ length: m + 1 }).map(() =>
    Array.from({ length: n + 1 }).map(() => false),
  );

  // 状态初始化
  // 1.空字符和空字符是匹配的
  dp[0][0] = true;
  for (let i = 1; i <= n; i++) {
    // 3.空字符串和*是匹配的 前一位可以匹配且当前位位为*才可以撇陪 p[i-1]代表 第i个字符,下标是i-1
    // p有3个字符如何能够匹配空字符串? 前面都是* 下面利用了已保存的状态;第三个字符,下标是2 p[i-1]
    dp[0][i] = dp[0][i - 1] && p[i - 1] == "*";
  }

  // 状态转移
  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (s[i - 1] == p[j - 1] || p[j - 1] == "?") {
        dp[i][j] = dp[i - 1][j - 1];
      } else if (p[j - 1] == "*") {
        dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
      }
    }
  }
  return dp[m][n];
};

六、正则经典

//经典正则
手机号验证(中国大陆)
/^1[3-9]\d{9}$/.test('13800138000')
邮箱验证
/^[\w-]+(.[\w-]+)*@[\w-]+(.[\w-]+)+$/.test('test@example.com')
身份证号验证(简单版)
/^\d{17}[\dXx]$/.test('11010119900307783X')
中文汉字匹配
/^[\u4e00-\u9fa5]+$/.test('中文测试')
提取域名
'https://example.com/path'.match(/https?:\/\/([^/]+)/)[1]
数字提取
'价格¥299'.match(/\d+/)[0]
日期格式验证(YYYY-MM-DD/^\d{4}-\d{2}-\d{2}$/.test('2025-09-28')
密码强度验证(6-12位字母数字)
/^[a-zA-Z0-9]{6,12}$/.test('Pass123')
HTML标签内容提取
'<div>content</div>'.match(/<[^>]+>([^<]+)</)[1]
空白字符替换
'多 个 空格'.replace(/\s+/g,'')

一百分、其他

面试方法论
    1、慢一点&&被提问环节至少问对方至少5个问题
    2、star法则 情景 目标 行动 结果
    3、总分总
    4、prep 观点 理由 举例子 总结

//通用获取类型
function getType(value) {
  return Object.prototype.toString.call(value)
    .match(/\s+(\w+)/)[1]  // 提取[object Type]中的Type
    .toLowerCase();        // 统一转为小写
}

闭包:函数能访问并记住其词法作用域,即使函数在作用域外执行。

前端性能优化
    代码压缩(gzip、丑化、多目标打包)
    图片懒加载(webp、雪碧图、base64)
    HTTP(http2、多静态资源域)
    缓存、CDN
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值