js代码大全:常用 JavaScript 示例与写法整理

js代码大全:常用 JavaScript 示例与写法整理

js代码大全:常用 JavaScript 示例与写法整理

JavaScript 常用于浏览器交互、表单处理、网络请求,也可以运行在 Node.js 等环境中。查找代码示例时,真正重要的不只是记住语法,还要了解代码依赖的环境、参数范围和失败情形。本文按常见任务整理可直接参考的写法,并说明容易忽略的边界。

示例以现代 JavaScript(ES2015 及之后的常见语法)为主。除 DOM 和浏览器存储部分外,多数片段可以放在浏览器控制台或 Node.js 中运行。示例中的变量名尽量独立;实际项目中应按业务修改,并配合测试。

一、变量、类型与默认值

优先使用 const 声明不会重新赋值的变量,确实需要重新赋值时使用 let。通常不建议继续使用 var:它的函数作用域和变量提升规则容易导致意外行为。注意,const 限制的是变量绑定,不能阻止对象内部属性变化。

const name = "小林";
let count = 0;
count += 1;

const settings = { theme: "light" };
settings.theme = "dark"; // 可以修改对象属性
// settings = {};        // 不可以给 const 变量重新赋值

console.log(name, count, settings.theme);

JavaScript 有 string、number、boolean、undefined、bigint、symbol 等原始类型,以及对象类型。null 表示有意设置的空值。值得注意的是,typeof null 的结果是 "object",这是语言历史遗留行为,不能用它判断是否为普通对象。

console.log(typeof "文字"); // "string"
console.log(typeof 42);     // "number"
console.log(typeof null);   // "object"

const value = null;
if (value === null) {
  console.log("明确的空值");
}

数值计算使用 IEEE 754 双精度浮点数,部分小数不能精确表示,例如 0.1 + 0.2 不严格等于 0.3。金额计算可按最小货币单位使用整数(如分),或采用经过评估的十进制定点方案,不能直接依赖浮点数严格相等。

二、条件判断与安全的默认值

严格相等运算符 === 不做类型转换,通常比 == 更容易推理。逻辑或 || 会把 0、空字符串和 false 都当作需要回退的值;如果只有 null 或 undefined 才应该使用默认值,应选择空值合并运算符 ??。

const pageFromInput = 0;

const pageA = pageFromInput || 1; // 1:0 被视为假值
const pageB = pageFromInput ?? 1; // 0:只对 null 和 undefined 回退

console.log(pageA, pageB);

读取可能不存在的嵌套属性时,可用可选链 ?. 避免因中间值为空而抛出异常。但它不会验证数据结构是否符合业务要求,也不会代替输入校验。

const user = {
  profile: {
    displayName: "小林"
  }
};

const displayName = user.profile?.displayName ?? "访客";
console.log(displayName);

不要将外部输入直接当作可信数据。来自表单、URL、接口响应或本地存储的值都可能缺失、格式错误或被修改。默认值适合处理缺省情况,不等于完整校验。

三、字符串处理与模板字符串

模板字符串适合拼接文本和表达式,换行也会保留。trim() 可去除字符串两端的空白,但不会清理字符串中间的空格;includes() 可检查子串是否存在。字符串方法通常返回新字符串,不会直接改写原字符串。

const rawName = "  小林  ";
const name = rawName.trim();
const message = `你好,${name}!`;

console.log(message);
console.log("javascript".includes("script")); // true
console.log("a,b,c".split(","));             // ["a", "b", "c"]

如果要把字符串转换成数字,不要仅依赖全局 isNaN,它会先进行类型转换。Number.isFinite() 可用于检查结果是否为有限数字。对于整数参数,还应明确允许范围,并处理空字符串等输入。

function parsePositiveInteger(input) {
  const text = String(input).trim();

  if (text === "") {
    return null;
  }

  const value = Number(text);
  if (!Number.isSafeInteger(value) || value <= 0) {
    return null;
  }

  return value;
}

console.log(parsePositiveInteger("12"));  // 12
console.log(parsePositiveInteger("1.5")); // null
console.log(parsePositiveInteger(""));    // null

此例允许可转换为正安全整数的输入;如果业务只接受纯数字文本,还需在转换前用正则或逐字符规则限制格式。正则表达式应按需求设计,避免过于宽泛的匹配。

四、数组常用操作

map() 用于把数组每项转换成新数组;filter() 用于筛选;find() 返回第一个匹配项,找不到时返回 undefined。这些方法不会修改原数组,但回调函数仍可能修改数组项所引用的对象。

const products = [
  { id: 1, name: "笔记本", price: 80 },
  { id: 2, name: "键盘", price: 120 },
  { id: 3, name: "鼠标", price: 50 }
];

const names = products.map(product => product.name);
const affordable = products.filter(product => product.price <= 80);
const keyboard = products.find(product => product.name === "键盘");

console.log(names);
console.log(affordable);
console.log(keyboard);

reduce() 可用于累计求和,但回调参数顺序容易写错:依次为累计值、当前项、索引和原数组。必须提供初始值,尤其是数组可能为空时;不提供初始值的空数组调用会抛出异常。

const prices = [10, 20, 30];

const total = prices.reduce((sum, price) => {
  return sum + price;
}, 0);

console.log(total); // 60

要对数组去重,常见做法是使用 Set。它按值识别基本类型;对对象来说,只有同一个对象引用才会被视为重复,不会按对象内容自动去重。

const values = [1, 2, 2, "2", 3];
const uniqueValues = [...new Set(values)];

console.log(uniqueValues); // [1, 2, "2", 3]

const first = { id: 1 };
const second = { id: 1 };
console.log(new Set([first, second]).size); // 2

五、对象、展开语法与解构

对象展开语法适合创建浅拷贝或合并对象。后面的属性会覆盖前面的同名属性;嵌套对象仍然共享引用,因此它不是深拷贝。

const defaults = { theme: "light", pageSize: 20 };
const options = { pageSize: 50 };

const config = { ...defaults, ...options };
console.log(config); // { theme: "light", pageSize: 50 }

const original = { user: { name: "小林" } };
const copy = { ...original };
copy.user.name = "小周";

console.log(original.user.name); // "小周",嵌套对象仍共享

对象解构可以简化取值,重命名和默认值也很常用。解构默认值只在取值为 undefined 时生效,不会覆盖 null。

const response = { data: { title: "文章" } };
const { data: { title = "无标题" } = {} } = response;

console.log(title);

const { limit = 10 } = { limit: null };
console.log(limit); // null

当对象属性名来自外部输入时,避免把不可信属性名随意写入普通对象;某些特殊属性名可能造成意外行为。可使用 Map 保存任意键值,或在确有需要时使用无原型对象,并对键名和数据进行校验。

六、函数、参数校验与异常

函数参数应有明确约定。下面的示例要求传入非负有限数值,不接受数字字符串;遇到不符合约定的参数会抛出 TypeError,由调用者决定如何处理。

function calculateDiscount(price, rate = 0.1) {
  if (typeof price !== "number" || !Number.isFinite(price) || price < 0) {
    throw new TypeError("price 必须是非负有限数值");
  }

  if (typeof rate !== "number" || !Number.isFinite(rate) ||
      rate < 0 || rate > 1) {
    throw new RangeError("rate 必须在 0 到 1 之间");
  }

  return price * (1 - rate);
}

console.log(calculateDiscount(100, 0.2)); // 80

这里的折扣比例范围为 0 到 1,端点均允许;业务若只允许小于 1,需要相应调整判断。抛出异常后如果没有被捕获,会中断当前调用链,因此不要用空的 catch 悄悄吞掉错误。

try {
  const result = calculateDiscount(100, 2);
  console.log(result);
} catch (error) {
  console.error("计算失败:", error.message);
}

纯函数对相同输入产生相同输出,并尽量不修改外部状态,更容易测试。实际函数如果会读写网络、时间、随机数或 DOM,就不是纯函数;把这些副作用集中在边界处,通常有利于维护。

七、日期与格式化

Date 适合处理时间戳和日期时间,但日期字符串的解析存在格式差异。优先使用带时区信息的标准格式或数值时间戳;对用户输入的日期,应按明确格式拆分和校验。注意月份在 Date 构造函数中从 0 开始计数。

const now = new Date();
console.log(now.toISOString()); // UTC 时间的 ISO 格式

const date = new Date(2025, 0, 15); // 本地时间:2025 年 1 月 15 日
console.log(date.getFullYear());
console.log(date.getMonth() + 1); // 输出月份时加 1

服务器存储和跨时区传输时,通常使用 UTC 时间戳或包含时区的 ISO 字符串;展示时再按用户时区格式化。不要把本地时间和 UTC 时间混为一谈,否则夏令时和时区差异可能导致日期偏移。

const formatter = new Intl.DateTimeFormat("zh-CN", {
  year: "numeric",
  month: "2-digit",
  day: "2-digit"
});

console.log(formatter.format(new Date()));

八、Promise 与 async/await

异步操作常以 Promise 表示。使用 async 声明的函数总会返回 Promise;在函数内部用 await 等待结果。try...catch 可处理等待期间的拒绝,但应保留有用的错误信息,而不是只显示“失败”。

function wait(milliseconds) {
  return new Promise(resolve => {
    setTimeout(resolve, milliseconds);
  });
}

async function runTask() {
  console.log("开始");
  await wait(300);
  console.log("结束");
}

runTask().catch(error => {
  console.error("任务出错:", error);
});

await 会暂停当前异步函数的后续执行。如果多个任务彼此独立,不必依次等待,可以使用 Promise.all() 并行等待;其中一个 Promise 拒绝时,Promise.all() 会拒绝,但这并不意味着其他任务自动取消。

async function loadBoth() {
  const [first, second] = await Promise.all([
    wait(200).then(() => "第一个完成"),
    wait(300).then(() => "第二个完成")
  ]);

  console.log(first, second);
}

loadBoth().catch(console.error);

循环中需要逐个按顺序处理异步任务时,可使用 for...of 配合 await。不需要顺序依赖时,可以先生成任务再交给 Promise.all();不要误以为 forEach 会等待异步回调完成。

九、使用 fetch 请求接口

fetch() 用于发起网络请求,浏览器和现代 Node.js 环境均提供该 API,但具体支持情况取决于运行时版本。一个常见误区是认为 HTTP 404 会让 Fetch Promise 自动拒绝;实际上通常需要检查 response.ok。网络错误、跨域限制和主动中止则可能进入异常处理。

async function getJson(url) {
  const response = await fetch(url, {
    headers: { Accept: "application/json" }
  });

  if (!response.ok) {
    throw new Error(`HTTP 错误:${response.status}`);
  }

  return response.json();
}

getJson("https://jsonplaceholder.typicode.com/todos/1")
  .then(data => console.log(data))
  .catch(error => console.error("请求失败:", error.message));

该示例依赖网络访问,目标服务器还必须允许当前环境请求。response.json() 本身也是异步操作;若响应不是合法 JSON,它会拒绝。生产代码还应根据接口约定检查响应字段和类型,不能只因为解析成功就认定数据可信。

使用 AbortController 可在支持它的环境中取消请求。超时不是 Fetch 默认提供的参数,需要自己创建定时器并在完成后清理。

async function fetchWithTimeout(url, timeoutMs = 5000) {
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
    throw new RangeError("timeoutMs 必须是正的有限数值");
  }

  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const response = await fetch(url, { signal: controller.signal });
    if (!response.ok) {
      throw new Error(`HTTP 错误:${response.status}`);
    }
    return await response.json();
  } finally {
    clearTimeout(timer);
  }
}

取消请求时,错误类型和具体表现可能随运行环境不同;调用方应把主动取消与普通网络失败区分处理。不要把访问令牌等秘密放进公开的前端代码或 URL 参数中,浏览器代码对用户并不保密。

十、浏览器 DOM 与事件

下面的代码仅适用于浏览器页面,且要求页面中存在对应元素。通过 querySelector() 获取的结果可能为 null,绑定事件前应检查。把文本显示到页面时,优先使用 textContent,它会作为文本处理,而不会将字符串解析成 HTML。

const button = document.querySelector("#save-button");
const output = document.querySelector("#output");

if (button && output) {
  button.addEventListener("click", () => {
    output.textContent = "已保存";
  });
}

不要把不可信输入直接赋给 innerHTML。它会解析 HTML,可能导致跨站脚本风险;只有在确实需要插入 HTML 且内容经过可靠消毒时才考虑使用。转义、消毒和上下文编码不是同一件事,具体做法应结合输出位置和框架安全机制。

事件监听器可在不再需要时移除。若页面组件反复创建和销毁却不清理监听器,可能出现重复响应或持有不必要引用。

const handleClick = () => {
  console.log("按钮被点击");
};

button?.addEventListener("click", handleClick);

// 不再需要时:
// button?.removeEventListener("click", handleClick);

事件委托适合处理数量较多、动态添加的子元素:把监听器放在稳定的父元素上,再检查事件目标。以下示例要求目标元素具有 data-action 属性。

const list = document.querySelector("#list");

list?.addEventListener("click", event => {
  const target = event.target;
  if (!(target instanceof Element)) return;

  const item = target.closest("[data-action='remove']");
  if (item && list.contains(item)) {
    item.closest("li")?.remove();
  }
});

十一、本地存储与 JSON

localStorage 只能保存字符串,数据通常需要通过 JSON.stringify() 和 JSON.parse() 转换。读取可能返回 null;内容也可能损坏,或因浏览器策略、存储空间等原因无法写入,所以读写都应考虑异常。

const storageKey = "app-settings";

function saveSettings(settings) {
  try {
    localStorage.setItem(storageKey, JSON.stringify(settings));
    return true;
  } catch (error) {
    console.error("保存失败:", error);
    return false;
  }
}

function loadSettings() {
  try {
    const text = localStorage.getItem(storageKey);
    if (text === null) return null;
    return JSON.parse(text);
  } catch (error) {
    console.error("读取或解析失败:", error);
    return null;
  }
}

saveSettings({ theme: "dark" });
console.log(loadSettings());

该代码要求运行在浏览器页面中。JSON 不能表示函数、undefined、循环引用等所有 JavaScript 值;序列化结果也不应被当作经过验证的业务对象。加载后仍要校验字段。localStorage 不适合保存密码、访问令牌或其他敏感凭据,因为同源脚本可读取它,存在跨站脚本等风险。

十二、常用小工具函数

防抖(debounce)用于把短时间内连续触发的调用合并为一次,适合搜索框输入、窗口尺寸变化等场景。下例会在最后一次调用后的指定时间执行。延迟必须是非负有限数值;使用时需留意组件销毁后定时器仍可能触发。

function debounce(fn, delay = 300) {
  if (typeof fn !== "function") {
    throw new TypeError("fn 必须是函数");
  }
  if (!Number.isFinite(delay) || delay < 0) {
    throw new RangeError("delay 必须是非负有限数值");
  }

  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

const searchLater = debounce(query => {
  console.log("搜索:", query);
}, 300);

searchLater("java");
searchLater("javascript");

节流(throttle)用于限制函数在一段时间内最多执行一次,适合滚动或拖动等高频事件。下面的简单实现按固定间隔执行一次,不保证最后一次调用一定补执行;如果业务要求尾部执行或立即执行,需要选用经过测试的实现并明确规则。

function throttle(fn, interval = 200) {
  if (typeof fn !== "function") {
    throw new TypeError("fn 必须是函数");
  }
  if (!Number.isFinite(interval) || interval < 0) {
    throw new RangeError("interval 必须是非负有限数值");
  }

  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

这两个函数保留调用时的 this 和参数,但没有提供取消待执行任务的接口。若页面卸载、组件销毁或输入内容变化时必须取消,应扩展 API 提供清理方法,或采用成熟库中语义明确的实现。

十三、随机数与安全边界

Math.random() 返回大于等于 0 且小于 1 的伪随机数,可用于一般界面效果或非安全用途的随机选择;它不适合生成密码、令牌或其他安全凭据。密码学用途应使用运行环境提供的安全随机 API,例如浏览器的 crypto.getRandomValues(),并遵循相应协议的字节数和编码要求。

function randomIndex(length) {
  if (!Number.isSafeInteger(length) || length <= 0) {
    throw new RangeError("length 必须是正安全整数");
  }
  return Math.floor(Math.random() * length);
}

console.log(randomIndex(5)); // 0 到 4

这段代码适用于不要求安全性的均匀索引选择;不要把它改名为“安全随机数”用于令牌或验证码保护。即使改用安全随机源,也需要正确处理取值范围,避免简单取模带来的分布偏差。

十四、常见错误检查清单

  • 比较值时优先使用 ===,并明确是否需要把数字字符串转换成数值。
  • 不要把 || 当成只处理空值的运算符;允许 0 或空字符串时考虑 ??。
  • 数组可能为空时,为 reduce() 提供初始值;需要结果时不要误用不等待异步回调的 forEach()。
  • 使用 Fetch 时检查 response.ok,并处理网络错误、JSON 解析失败和取消请求。
  • 外部数据需要校验类型、范围和结构;默认值不能代替校验。
  • 展示不可信文本优先使用 textContent,避免把未经处理的内容写入 innerHTML。
  • 对象展开是浅拷贝;日期、浮点数和时区运算都要明确业务规则。
  • 不要在前端代码中放置需要保密的密钥,也不要将 Math.random() 用于安全凭据。

JavaScript 示例可以作为实现起点,但不能替代接口约定、输入验证和安全评估。把参数范围写清楚,在边界输入和失败路径上做测试,再根据目标浏览器或 Node.js 版本验证运行结果,通常比直接复制一段“万能代码”更可靠。

© 版权声明

相关文章