三十的博客

JavaScript 函数高级技巧(系列02)

本文内容基于 AI 生成结果整理,可能包含不准确信息,仅供参考使用。
发布时间
阅读量 加载中...

一、箭头函数特性

1. 与传统函数区别

javascript
// 传统函数
function sum(a, b) {
  return a + b;
}

// 箭头函数
const sum = (a, b) => a + b;

关键差异 ​:

2. 适合场景举例

javascript
// 1. 数组操作
const nums = [1, 2, 3];
const doubled = nums.map((n) => n * 2);

// 2. 定时器
setTimeout(() => {
  console.log("延迟执行");
}, 1000);

二、闭包实战应用

1. 基础闭包示例

javascript
function createCounter() {
  let count = 0;
  return function () {
    return ++count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

2. 封装私有变量

javascript
function createUser(name) {
  let age = 20; // 私有变量
  return {
    getName: () => name,
    getAge: () => age,
    setAge: (newAge) => {
      age = newAge;
    },
  };
}

const user = createUser("小明");
console.log(user.getAge()); // 20
user.setAge(21);
console.log(user.age); // undefined(无法直接访问)

三、递归优化策略

1. 基础递归(存在栈溢出风险)

javascript
function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}
console.log(factorial(5)); // 120

2. 尾递归优化(ES6)

javascript
function factorial(n, total = 1) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}
console.log(factorial(5)); // 120

优化原理 ​:

四、函数柯里化

柯里化(Currying)是一种将多参数函数转换为一系列单参数函数的技术,使得函数可以逐步接收参数,并在所有参数收集完毕后执行。

柯里化的核心价值:

1. 基础柯里化实现

javascript
function curry(fn) {
  return function curried(...args) {
    return args.length >= fn.length
      ? fn(...args)
      : (...moreArgs) => curried(...args, ...moreArgs);
  };
}

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

2. 实际应用场景

示例 1:创建特定前缀的日志函数

javascript
// 创建特定前缀的日志函数
const createLogger = (prefix) => (message) => {
  console.log(`[${prefix}] ${message}`);
};

const appLogger = createLogger("APP");
appLogger("启动成功"); // [APP] 启动成功

作用 ​:

示例 2:动态生成 API 请求函数

javascript
const createApiRequest = (baseUrl) => (endpoint) => (params) => {
  return fetch(`${baseUrl}/${endpoint}`, { body: JSON.stringify(params) });
};

// 固定 baseUrl
const myApi = createApiRequest("https://api.example.com");

// 固定 endpoint
const getUser = myApi("user");
const getPosts = myApi("posts");

// 最终调用
getUser({ id: 123 }); // 请求 https://api.example.com/user
getPosts({ page: 1 }); // 请求 https://api.example.com/posts

作用 ​:

示例 3:校验表单字段(复用校验规则)

javascript
const validateField = (rule) => (value) => {
  return rule.test(value);
};

// 固定校验规则
const isEmail = validateField(/^\S+@\S+\.\S+$/);
const isPhone = validateField(/^1\d{10}$/);

// 使用
console.log(isEmail("test@example.com")); // true
console.log(isPhone("13800138000")); // true

作用 ​:

示例 4:动态生成 CSS 类名(React/Vue 中常用)​

javascript
const addClassName = (baseClass) => (modifier) => {
  return `${baseClass} ${baseClass}--${modifier}`;
};

// 固定基础类名
const btnClass = addClassName("btn");

// 生成具体类名
btnClass("primary"); // "btn btn--primary"
btnClass("danger"); // "btn btn--danger"

作用 ​:

示例 5:计算折扣价格(电商场景)

javascript
const applyDiscount = (discount) => (price) => {
  return price * (1 - discount);
};

// 固定折扣率
const summerSale = applyDiscount(0.2); // 8 折
const blackFriday = applyDiscount(0.5); // 5 折

// 计算实际价格
summerSale(100); // 80 (100 * 0.8)
blackFriday(100); // 50 (100 * 0.5)

作用 ​:

示例 6:权限检查(前端鉴权)

javascript
const checkPermission = (role) => (resource) => {
  return role === "admin" || resource.accessibleTo.includes(role);
};

// 固定用户角色
const userCanAccess = checkPermission("user");
const adminCanAccess = checkPermission("admin");

// 检查权限
userCanAccess({ accessibleTo: ["user", "admin"] }); // true
adminCanAccess({ accessibleTo: ["admin"] }); // true

作用 ​:

五、性能优化技巧

1. 记忆化缓存

javascript
function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    return cache.has(key)
      ? cache.get(key)
      : cache.set(key, fn(...args)).get(key);
  };
}

// 使用
const fib = memoize((n) => (n <= 1 ? 1 : fib(n - 1) + fib(n - 2)));
console.log(fib(50)); // 快速计算

2. 防抖与节流

特性 防抖(Debounce) 节流(Throttle)
触发时机 停止操作后 delay 毫秒执行 固定间隔 interval 毫秒执行一次
重置计时 每次新触发会重置计时 不会重置计时
适用场景 搜索联想、窗口调整 滚动、鼠标移动、游戏按键
极端情况 如果一直触发,则永远不会执行 至少每隔 interval 执行一次

防抖

作用 ​: 事件触发后,等待 delay 毫秒再执行,如果期间再次触发,则重新计时。

适用场景 ​: 搜索框输入联想、窗口大小调整、避免按钮重复提交。

javascript
function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer); // 清除之前的计时
    timer = setTimeout(() => fn(...args), delay); // 重新开始计时
  };
}

// 使用示例:搜索框输入联想
const searchInput = document.getElementById("search");
const fetchResults = (query) => {
  console.log("搜索:", query);
  // 实际场景中这里可能是 AJAX 请求
};

// 防抖后的搜索函数(停止输入 500ms 后才会触发)
const debouncedSearch = debounce(fetchResults, 500);
searchInput.addEventListener("input", (e) => debouncedSearch(e.target.value));

// 测试输入 "hello"(快速连续输入时,只会在停止后打印一次)
// 输入:h -> e -> l -> l -> o (500ms 内无新输入)
// 输出:搜索:hello

节流

作用 ​: 固定时间间隔(interval)内最多执行一次,稀释事件触发频率。

适用场景 ​: 滚动事件监听、鼠标移动跟踪、游戏中的按键响应。

javascript
function throttle(fn, interval) {
  let lastTime = 0;
  return (...args) => {
    const now = Date.now();
    if (now - lastTime >= interval) {
      // 如果距离上次执行已超过间隔
      fn(...args);
      lastTime = now; // 更新最后执行时间
    }
  };
}

// 使用示例:滚动事件监听
const handleScroll = () => {
  console.log("滚动位置:", window.scrollY);
};

// 节流后的滚动函数(每 200ms 最多触发一次)
const throttledScroll = throttle(handleScroll, 200);
window.addEventListener("scroll", throttledScroll);

// 测试快速滚动页面
// 实际输出(假设用户疯狂滚动):
// 滚动位置:100
// 滚动位置:300 (200ms 后)
// 滚动位置:500 (再过 200ms 后)
// ...(不会因为高频滚动导致回调爆炸)

高级用法(结合二者)​

javascript
// 增强版:结合防抖和节流(先节流,最后一次触发防抖)
function hybrid(fn, delay) {
  let lastTime = 0;
  let timer;
  return (...args) => {
    const now = Date.now();
    if (now - lastTime < delay) {
      clearTimeout(timer);
      timer = setTimeout(() => {
        fn(...args);
        lastTime = now;
      }, delay);
    } else {
      fn(...args);
      lastTime = now;
    }
  };
}

// 使用场景:既希望限制频率,又希望最终触发一次(如实时保存表单)
const saveForm = hybrid(() => console.log("保存数据"), 1000);

实际应用场景

场景 1:按钮防重复提交

javascript
const submitOrder = debounce(() => {
  console.log("订单提交中...");
  // 实际发送请求
}, 1000);

document.getElementById("buy-btn").addEventListener("click", submitOrder);
// 用户疯狂点击按钮时,只会提交一次

场景 2:无限滚动加载(节流)

javascript
const checkScrollBottom = throttle(() => {
  if (window.scrollY + window.innerHeight >= document.body.scrollHeight) {
    console.log("加载更多数据...");
  }
}, 500);

window.addEventListener("scroll", checkScrollBottom);
// 滚动到底部时,最多每 500ms 检查一次
#Javascript