伟大的日常

一、永恒的经典

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 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]=cb
        }
        this.events[evnet].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=[]
    for(const task of tasks){
        const runningTask=task().then(res=>{
            runningTasks.splice(runningTasks.indexOf(runningTask),1)
            results.push(res)
        })
        runningTasks.push(runningTask)
        if(runningTasks.length>=concurrency){
            console.log('并发啦')
            await Promise.race(runningTasks)
        }
    }
    return Promise.all(runningTasks).then(()=>results)
}
async function runTasks(tasks, concurrency) {
    const results = [],
        runningTasks = new Set()
    for (let i = 0; i < task.length; i++) {
        if (runningTasks.length >= concurrency) {
            await Promise.race(runningTasks)
        }
        const taskPromise = task[i]().then(res => {
            runningTasks.delete(taskPromise)
            results[i] = res
        })
        runningTasks.add(taskPromise)
    }
    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、付费专栏及课程。

余额充值