9 个 JavaScript 单行代码可替代 50 行代码

我们所有人,有时,看到一堵可怕的 JavaScript 代码,心里暗自咒骂,心里很清楚应该有更好的方法。

经过一段时间的学习,我发现了一些简洁的单行代码,它们可以省去很多冗长的代码。

这些是真正有用且易读的技巧,利用现代 JavaScript 功能来解决常见问题。

因此,无论您是清理代码还是刚开始一个新项目,这些技巧都可以帮助您编写更优雅、更易于维护的代码。

这里有 9 个您今天就可以使用的巧妙的俏皮话。

扁平化嵌套数组

有没有试过展平如此深的数组?过去,这意味着很多复杂的多重循环、临时数组,以及太多的代码。

但现在它在一个强大的单行代码中非常漂亮地执行:

const flattenArray = arr => arr.flat(Infinity);

const nestedArray = [1, [2, 3, [4, 5, [6, 7]]], 8, [9, 10]];
const cleanArray = flattenArray(nestedArray);
// Result: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

如果你用更传统的方式来做这件事,你将会得到如下的结果:

function flattenTheHardWay(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flattenTheHardWay(arr[i]));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}

所有繁琐的工作都由 flat() 处理,添加 Infinity 会告诉它下降到任何可能的水平。简单、干净,而且有效。

对象转换:无依赖关系的深度克隆

如果你需要一个真正的对象深度克隆,而不需要引入 lodash?这里有一个零依赖解决方案,可以处理嵌套对象、数组甚至日期:

const deepClone = obj => JSON.parse(JSON.stringify(obj));

const complexObj = {
  user: { name: 'Alex', date: new Date() },
  scores: [1, 2, [3, 4]],
  active: true
};

const cloned = deepClone(complexObj);

老办法?你必须输入如下内容:

function manualDeepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);

  const clone = Array.isArray(obj) ? [] : {};

  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      clone[key] = manualDeepClone(obj[key]);
    }
  }
  return clone;
}

**快速提示:**此单行代码确实有一些限制 - 它无法处理函数、符号或循环引用。但对于 90% 的使用情况,它几乎是正确的。

字符串处理:将 CSV 转换为对象数组

这是一个很棒的小单行代码,它接受 CSV 数据并输出可操作的对象数组,非常适合用于 API 响应或读取数据:

const csvToObjects = csv => csv.split('\n').map(row => Object.fromEntries(row.split(',').map((value, i, arr) => [arr[0].split(',')[i], value])));

const csvData = `name,age,city
Peboy,30,New York
Peace,25,San Francisco
Lara,35,Chicago`;

const parsed = csvToObjects(csvData);
// Result:
// [
//   { name: 'Peboy', age: '30', city: 'New York' },
//   { name: 'Peace', age: '25', city: 'San Francisco' },
//   { name: 'Lara', age: '35', city: 'Chicago' }
// ]

过时了吗?哦,你可能会写类似这样的东西:

function convertCSVTheHardWay(csv) {
  const lines = csv.split('\n');
  const headers = lines[0].split(',');
  const result = [];

  for (let i = 1; i < lines.length; i++) {
    const obj = {};
    const currentLine = lines[i].split(',');

    for (let j = 0; j < headers.length; j++) {
      obj[headers[j]] = currentLine[j];
    }
    result.push(obj);
  }
  return result;
}

这是一种使用单行命令进行数据转换的有效方法,但在投入生产之前需要添加一些错误处理。

数组操作:删除重复项和排序

这是一个缩短的单行代码,可以同时删除重复项并对您的数组进行排序,非常适合清理数据集:

const uniqueSorted = arr => [...new Set(arr)].sort((a, b) => a - b);

// Example of its use:
const messyArray = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
const cleaned = uniqueSorted(messyArray);
// Result: [1, 2, 3, 4, 5, 6, 9]

// For string sorting

const messyStrings = ['banana', 'apple', 'apple', 'cherry', 'banana'];
const cleanedStrings = [...new Set(messyStrings)].sort();
// Result: ['apple', 'banana', 'cherry']

以前的方式是这样的:

function cleanArrayManually(arr) {
  const unique = [];
  for (let i = 0; i < arr.length; i++) {
    if (unique.indexOf(arr[i]) === -1) {
      unique.push(arr[i]);
    }
  }
  return unique.sort((a, b) => a - b);
}

Set 完美地处理了重复项,然后扩展运算符将其重新转换为数组。之后您只需调用 sort() 即可!

DOM 操作:查询和转换多个元素

这是一个功能强大的单行代码,可以让你一次查询和转换多个 DOM 元素:

const modifyElements = selector => Array.from(document.querySelectorAll(selector)).forEach(el => el.style);

// Use it like this:
const updateButtons = modifyElements('.btn')
  .map(style => Object.assign(style, {
    backgroundColor: '#007bff',
    color: 'white',
    padding: '10px 20px'
}));

// Or even simpler for class updates:
const toggleAll = selector => document.querySelectorAll(selector).forEach(el => el.classList.toggle('active'));

传统方法是:

function updateElementsManually(selector) {
  const elements = document.querySelectorAll(selector);
  for (let i = 0; i < elements.length; i++) {
    const el = elements[i];
    el.style.backgroundColor = '#007bff';
    el.style.color = 'white';
    el.style.padding = '10px 20px';
  }
}

这适用于所有现代浏览器,并可节省您编写重复的 DOM 操作代码。

具有干净错误处理的并行 API 调用

这是另一条干净的线路,一行程序可以并行调用 API,并且以非常干净的错误处理方式执行。

const parallelCalls = urls => Promise.allSettled(urls.map(url => fetch(url).then(r => r.json())));


// Put it to work:
const urls = [
  'https://api.example.com/users',
  'https://api.example.com/posts',
  'https://api.example.com/comments'
];


// One line to fetch them all:
const getData = async () => {
  const results = await parallelCalls(urls);
  const [users, posts, comments] = results.map(r => r.status === 'fulfilled' ? r.value : null);
};

更详细的说法是:

async function fetchDataManually(urls) {
  const results = [];
  for (const url of urls) {
    try {
      const response = await fetch(url);
      const data = await response.json();
      results.push({ status: 'fulfilled', value: data });
    } catch (error) {
      results.push({ status: 'rejected', reason: error });
    }
  }
  return results;
}

`Promise.allSettled` 是这里的主角;如果一个请求失败它也不会失败,并且它会为每次调用返回干净的状态信息。

日期/时间格式:无需库即可清理日期字符串

这是一个很棒的单行代码,它可以将日期转换为干净、可读的字符串,而无需任何外部依赖:

const formatDate = date => new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'short' }).format(date);

const now = new Date();
console.log(formatDate(now));
// Output: "Thursday, December 3, 2024 at 2:30 PM"

// Want a different format? Easy:
const shortDate = date => new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric' }).format(date);
// Output: "Dec 3, 2024"

传统的做法是这样的:

function formatDateManually(date) {
  const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
  const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

  const dayName = days[date.getDay()];
  const monthName = months[date.getMonth()];
  const day = date.getDate();
  const year = date.getFullYear();
  const hours = date.getHours();
  const minutes = date.getMinutes();
  const ampm = hours >= 12 ? 'PM' : 'AM';

  return `${dayName}, ${monthName} ${day}, ${year} at ${hours % 12}:${minutes.toString().padStart(2, '0')} ${ampm}`;
}

`Intl.DateTimeFormat` 处理所有繁重工作,包括本地化。无需再手动构建日期字符串!

事件处理:消除抖动,避免臃肿

这是一个简洁的单行代码,可以创建任何函数的去抖动版本 - 非常适合搜索输入或窗口调整大小处理程序:

const debounce=(fn,ms)=>{let timeout;return(...args)=>{clearTimeout(timeout);timeout=setTimeout(()=>fn(...args),ms);};};

// Put it to work:
const expensiveSearch=query=>console.log('Searching for:',query);
const debouncedSearch=debounce(expensiveSearch,300);

// Use it in your event listener:
searchInput.addEventListener('input',e=>debouncedSearch(e.target.value));

传统的方式如下:

function createDebounce(fn, delay) {
  let timeoutId;

  return function debounced(...args) {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }

    timeoutId = setTimeout(() => {
      fn.apply(this, args);
      timeoutId = null;
    }, delay);
  };
}

这一行代码涵盖了所有基本的去抖动用例,并节省了您不必要地调用函数的时间,尤其是在输入或调整大小等快速连续生成输入时。

本地存储:带验证的对象存储

这里只是另一个简洁的单行代码,它使用内置验证和错误处理来处理 localStorage 中的对象存储:

const store = key => ({ get: () => JSON.parse(localStorage.getItem(key)), set: data => localStorage.setItem(key, JSON.stringify(data)), remove: () => localStorage.removeItem(key) });

// Use it like this:
const userStore = store('user');

// Save data
userStore.set({ id: 1, name: 'John', preferences: { theme: 'dark' } });

// Get data
const userData = userStore.get();

// Remove data
userStore.remove();

旧方法需要这样的东西:

function handleLocalStorage(key) {
  return {
    get: function() {
      try {
        const item = localStorage.getItem(key);
        return item ? JSON.parse(item) : null;
      } catch (e) {
        console.error('Error reading from localStorage', e);
        return null;
      }
    },
    set: function(data) {
      try {
        localStorage.setItem(key, JSON.stringify(data));
      } catch (e) {
        console.error('Error writing to localStorage', e);
      }
    },
    remove: function() {
      try {
        localStorage.removeItem(key);
      } catch (e) {
        console.error('Error removing from localStorage', e);
      }
    }
  };
}

包装器为您提供了一个用于 localStorage 操作的干净 API,并自动处理所有 JSON 解析/字符串化。

总结

这些单行代码不仅能减少代码编写量,还能让代码编写更智能。每行代码都能以简洁、可维护的方式解决常见的 JavaScript 难题。虽然这些代码片段功能强大,但请记住,可读性始终是第一位的。如果一行代码让您的代码更难理解,请将其拆分成多行。

您可以随意在您的项目中混合搭配这些模式,如果您支持旧版浏览器,请不要忘记检查浏览器对较新的 JavaScript 功能(如“flat()”或“Intl.DateTimeFormat”)的兼容性。

你有自己强大的 JavaScript 单行代码吗?我很想看看!