最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文分享10個被嚴(yán)重低估的JS特性

 更新時間:2025年11月04日 08:33:02   作者:冴羽  
最近逛?Reddit?的時候,看到一個關(guān)于最被低估的?JavaScript?特性的討論,因此本文就對此進行了總結(jié),和大家分享一下,希望對大家有所幫助

前言

最近逛 Reddit 的時候,看到一個關(guān)于最被低估的 JavaScript 特性的討論,我對此進行了總結(jié),和大家分享一下。

1. Set:數(shù)組去重 + 快速查找,比 filter 快 3 倍

提到數(shù)組去重,很多人第一反應(yīng)是 filter + indexOf,但這種寫法的時間復(fù)雜度是 O (n²),而 Set 天生支持 “唯一值”,查找速度是 O (1),還能直接轉(zhuǎn)數(shù)組。

舉個例子:

用戶 ID 去重:

// 后端返回的重復(fù)用戶 ID 列表
const duplicateIds = [101, 102, 102, 103, 103, 103];
// 1 行去重
const uniqueIds = [...new Set(duplicateIds)];
console.log(uniqueIds); // [101,102,103]

避免重復(fù)綁定事件:

const listenedEvents = new Set();
// 封裝事件綁定函數(shù),防止同一事件重復(fù)綁定
function safeAddEvent(eventName, handler) {
  if (!listenedEvents.has(eventName)) {
    window.addEventListener(eventName, handler);
    listenedEvents.add(eventName); // 標(biāo)記已綁定
  }
}
// 調(diào)用 2 次也只會綁定 1 次 scroll 事件
safeAddEvent("scroll", () => console.log("滾動了"));
safeAddEvent("scroll", () => console.log("滾動了"));

2. Object.entries () + Object.fromEntries ():對象數(shù)組互轉(zhuǎn)神器

以前想遍歷對象,要用 for...in 循環(huán),外加判斷 hasOwnProperty;如果想把數(shù)組轉(zhuǎn)成對象,只能手動寫循環(huán)。這對組合直接一鍵搞定。

舉個例子:

篩選對象屬性,過濾掉空值:

// 后端返回的用戶信息,包含空值字段
const userInfo = {
  name: "張三",
  age: 28,
  avatar: "", // 空值,需要過濾
  phone: "13800138000",
};
// 1. 轉(zhuǎn)成[key,value]數(shù)組,過濾空值;2. 轉(zhuǎn)回對象
const filteredUser = Object.fromEntries(Object.entries(userInfo).filter(([key, value]) => value !== ""));
console.log(filteredUser);
// {name: "張三", age:28, phone: "13800138000"}

URL 參數(shù)轉(zhuǎn)對象(不用再寫正則了)

// 地址欄的參數(shù):?name=張三&age=28&gender=男
const searchStr = window.location.search.slice(1);

// 直接轉(zhuǎn)成對象,支持中文和特殊字符
const paramObj = Object.fromEntries(new URLSearchParams(searchStr));

console.log(paramObj); // {name: "張三", age: "28", gender: "男"}

3. ?? 與 ??=:比 || 靠譜

|| 設(shè)置默認(rèn)值時,會把 0、""false這些 “有效假值” 當(dāng)成空值。比如用戶輸入 0(表示數(shù)量),count || 10會返回 10,但這里其實應(yīng)該返回 0。而??只判斷 null/undefined。

舉個例子:

處理用戶輸入的 “有效假值”:

// 用戶輸入的數(shù)量( 0 是有效數(shù)值,不能替換)
const userInputCount = 0;

// 錯誤寫法:會把 0 當(dāng)成空值,返回 10
const wrongCount = userInputCount || 10;

// 正確寫法:只判斷 null/undefined,返回 0
const correctCount = userInputCount ?? 10;

console.log(wrongCount, correctCount); // 10, 0

給對象補默認(rèn)值(不會覆蓋已有值):

// 前端傳入的配置,可能缺少 retries 字段
const requestConfig = { timeout: 5000 };

// 只有當(dāng) retries 為 null/undefined 時,才賦值 3(不覆蓋已有值)
requestConfig.retries ??= 3;
console.log(requestConfig); // {timeout:5000, retries:3}

// 如果已有值,不會被覆蓋
const oldConfig = { timeout: 3000, retries: 2 };
oldConfig.retries ??= 3;
console.log(oldConfig); // {timeout:3000, retries:2}

4. Intl API:原生國際化 API

很多人會用 moment.js 處理日期、貨幣格式化,但這個庫體積特別大(壓縮后也有幾十 KB);而 Intl 是瀏覽器原生 API,支持貨幣、日期、數(shù)字的本地化,體積為 0,還能自動適配地區(qū)。

舉個例子:

多語言貨幣格式化(適配中英文):

const price = 1234.56;

// 人民幣格式(自動加 ¥ 和千分位)
const cnyPrice = new Intl.NumberFormat("zh-CN", {
  style: "currency",
  currency: "CNY",
}).format(price);

// 美元格式(自動加 $ 和千分位)
const usdPrice = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
}).format(price);

console.log(cnyPrice, usdPrice); // ¥1,234.56 $1,234.56

日期本地化(不用手動拼接年月日):

const now = new Date();

// 中文日期:2025年11月3日 15:40:22
const cnDate = new Intl.DateTimeFormat("zh-CN", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "2-digit",
  minute: "2-digit",
  second: "2-digit",
}).format(now);

// 英文日期:November 3, 2025, 03:40:22 PM
const enDate = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "2-digit",
  minute: "2-digit",
  second: "2-digit",
}).format(now);
console.log(cnDate, enDate);

5. Intersection Observer:圖片懶加載 + 滾動加載,不卡主線程

傳統(tǒng)我們用 scroll事件 + getBoundingClientRect()判斷元素是否在視口,會頻繁觸發(fā)重排,導(dǎo)致頁面卡頓;Intersection ObserverAPI 是異步監(jiān)聽,不阻塞主線程,性能直接提升一大截。

舉個例子:

圖片懶加載(可用于優(yōu)化首屏加載速度):

<!-- 用data-src存真實圖片地址,src放占位圖 -->
<img data-src="https://xxx.com/real-img.jpg" src="placeholder.jpg" class="lazy-img" />
// 初始化觀察者
const lazyObserver = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    // 當(dāng)圖片進入視口
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src; // 加載真實圖片
      lazyObserver.unobserve(img); // 加載后停止監(jiān)聽
    }
  });
});
// 給所有懶加載圖片添加監(jiān)聽
document.querySelectorAll(".lazy-img").forEach((img) => {
  lazyObserver.observe(img);
});

列表滾動加載更多(避免一次性加載過多數(shù)據(jù)):

<ul id="news-list"></ul>
<!-- 加載提示,放在列表底部 -->
<div id="load-more">加載中...</div>
const loadObserver = new IntersectionObserver((entries) => {
  if (entries[0].isIntersecting) {
    // 當(dāng)加載提示進入視口,請求下一頁數(shù)據(jù)
    fetchNextPageData().then((data) => {
      renderNews(data); // 渲染新列表項
    });
  }
});
// 監(jiān)聽加載提示元素
loadObserver.observe(document.getElementById("load-more"));

6. Promise.allSettled ():批量請求不 “掛掉”,比 Promise.all 更實用

如果使用 Promise.all,當(dāng)批量請求時,只要有一個請求失敗,Promise.all 就會直接 reject,其他成功的請求結(jié)果就拿不到了;而 allSettled 會等待所有請求完成,不管成功失敗,還能分別處理結(jié)果。

舉個例子:

批量獲取用戶信息 + 訂單 + 消息(部分接口失敗不影響整體):

// 3個并行請求,可能有失敗的
const requestList = [
  fetch("/api/user/101"), // 成功
  fetch("/api/orders/101"), // 失敗(比如訂單不存在)
  fetch("/api/messages/101"), // 成功
];

// 等待所有請求完成,處理成功和失敗的結(jié)果
Promise.allSettled(requestList).then((results) => {
  // 處理成功的請求
  const successData = results.filter((res) => res.status === "fulfilled").map((res) => res.value.json());
  // 記錄失敗的請求(方便排查問題)
  const failedRequests = results.filter((res) => res.status === "rejected").map((res) => res.reason.url);
  console.log("成功數(shù)據(jù):", successData);
  console.log("失敗接口:", failedRequests); // ["/api/orders/101"]
});

7. element.closest ():向上找父元素最安全的方式

傳統(tǒng)如果想找某個元素的父元素,比如點擊列表項找列表,需要使用 element.parentNode.parentNode,但一旦 DOM 結(jié)構(gòu)變了,代碼就崩了;closest() 回直接根據(jù) CSS 選擇器找最近的祖先元素,不管嵌套多少層。

舉個例子:

點擊列表項,給列表容器加高亮:

<ul class="user-list">
  <li class="user-item">張三</li>
  <li class="user-item">李四</li>
</ul>
document.querySelectorAll(".user-item").forEach((item) => {
  item.addEventListener("click", (e) => {
    // 找到最近的.user-list(不管中間嵌套多少層)
    const list = e.target.closest(".user-list");
    list.classList.toggle("active"); // 切換高亮
  });
});

輸入框聚焦,給表單組加樣式:

<div class="form-group">
  <label>用戶名</label>
  <input type="text" id="username" />
</div>
const usernameInput = document.getElementById("username");
usernameInput.addEventListener("focus", (e) => {
  // 找到最近的.form-group,加focused樣式
  const formGroup = e.target.closest(".form-group");
  formGroup.classList.add("focused");
});

8. URL + URLSearchParams:處理 URL 方便多了

傳統(tǒng)解析 URL 參數(shù)、修改參數(shù),還要寫復(fù)雜的正則表達(dá)式,有時還得處理中文編碼問題;當(dāng)然我們會直接引入三方庫來處理,但畢竟還要引入多余的苦,其實 URL API 可以直接解析 URL 結(jié)構(gòu),URLSearchParams 可用于處理參數(shù),支持增刪改查,自動編碼,方便多了。

解析 URL 參數(shù)(支持中文和特殊字符):

// 當(dāng)前頁面URL:https://xxx.com/user?name=張三&age=28&gender=男
const currentUrl = new URL(window.location.href);

// 獲取參數(shù)
console.log(currentUrl.searchParams.get("name")); // 張三
console.log(currentUrl.hostname); // xxx.com(域名)
console.log(currentUrl.pathname); // /user(路徑)

修改 URL 參數(shù),跳轉(zhuǎn)新頁面:

const url = new URL("https://xxx.com/list");

// 添加參數(shù)
url.searchParams.append("page", 2);
url.searchParams.append("size", 10);

// 修改參數(shù)
url.searchParams.set("page", 3);

// 刪除參數(shù)
url.searchParams.delete("size");
console.log(url.href); // https://xxx.com/list?page=3
window.location.href = url.href; // 跳轉(zhuǎn)到第3頁

9. for...of 循環(huán):比 forEach 靈活,還支持 break 和 continue

我們都知道,forEach 不能用 break中斷循環(huán),也不能用 continue跳過當(dāng)前項。而for...of不僅支持中斷,還能遍歷數(shù)組、Set、Map、字符串,甚至獲取索引。

舉個例子:

遍歷數(shù)組,找到目標(biāo)值后中斷:

const productList = [
  { id: 1, name: "手機", price: 5999 },
  { id: 2, name: "電腦", price: 9999 },
  { id: 3, name: "平板", price: 3999 },
];

// 找價格大于8000的產(chǎn)品,找到后中斷
for (const product of productList) {
  if (product.price > 8000) {
    console.log("找到高價產(chǎn)品:", product); // {id:2, name:"電腦", ...}
    break; // 中斷循環(huán),不用遍歷剩下的
  }
}

遍歷 Set,獲取索引:

const uniqueTags = new Set(["前端", "JS", "CSS"]);

// 用 entries() 獲取索引和值
for (const [index, tag] of [...uniqueTags].entries()) {
  console.log(`索引${index}:${tag}`); // 索引 0:前端,索引 1:JS...
}

10. 頂層 await:模塊異步初始化

以前在 ES 模塊里想異步加載配置,必須寫個 async 函數(shù)再調(diào)用;現(xiàn)在 top-level await 允許你在模塊頂層直接用 await,其他模塊導(dǎo)入時會自動等待,不用再手動處理異步。

舉個例子:

模塊初始化時加載配置:

// config.js
// 頂層直接 await,加載后端配置

const response = await fetch("/api/config");
export const appConfig = await response.json(); // {baseUrl: "https://xxx.com", timeout: 5000}
// api.js(導(dǎo)入 config.js,自動等待配置加載完成)
import { appConfig } from "./config.js";

// 直接用配置,不用關(guān)心異步
export const apiClient = {
  baseUrl: appConfig.baseUrl,
  get(url) {
    return fetch(`${this.baseUrl}${url}`, { timeout: appConfig.timeout });
  },
};

點擊按鈕動態(tài)加載組件(按需加載,減少首屏體積):

// 點擊“圖表”按鈕,才加載圖表組件
document.getElementById("show-chart-btn").addEventListener("click", async () => {
  // 動態(tài)導(dǎo)入圖表模塊,await 等待加載完成
  const { renderChart } = await import("./chart-module.js");
  renderChart("#chart-container"); // 渲染圖表
});

結(jié)語

可以看到,以前我們依賴的第三方庫,其實原生 API 早就能解決,比如用 Intl 替代 moment.js,用 Set 替代 lodash 的 uniq,用 Intersection Observer 替代懶加載,隨著老舊的瀏覽器被討論,兼容性越來越好,這些 API 以后會成為基操。

以上就是一文分享10個被嚴(yán)重低估的JS特性的詳細(xì)內(nèi)容,更多關(guān)于JS特性的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

余干县| 合作市| 安泽县| 延津县| 龙口市| 太白县| 翁源县| 积石山| 大余县| 丰宁| 水富县| 嘉祥县| 垦利县| 合阳县| 马鞍山市| 鄂伦春自治旗| 商河县| 江城| 庆元县| 东宁县| 璧山县| 盐源县| 三台县| 滕州市| 延长县| 灵璧县| 思茅市| 额尔古纳市| 博客| 上饶市| 林口县| 双峰县| 广元市| 彰化市| 濉溪县| 温州市| 绥阳县| 桂阳县| 云安县| 佛冈县| 那坡县|