# 常用 JS 函数

### h5 页面嵌入 QQ 客服代码

```markup
<a href="mqqwpa://im/chat?chat_type=wpa&uin={{yourQQNumber}}&version=1&src_type=web&web_src=oicqzone.com"></a>
```

### 将一个数组分为多个数组

示例：\[1,2,3,4,5,6,7,8,9,10] 转换为 \[\[1, 2, 3, 4], \[5, 6, 7, 8], \[9, 10]]

```javascript
const sliceArray = function (arr, num) {
  const result = [];
  for (let i = 0; i < arr.length; i += num) {
    result.push(arr.slice(i, i + num));
  }
  return result;
};
```

### 限制只能传入数字

```javascript
// valStr 确保是一个字符串类型
const limitToNumberStr = function (valStr) {
  let tempValue = valStr.toString();
  return tempValue
    .replace(/[^0-9.]/g, "")
    .replace(".", "#*")
    .replace(/\./g, "")
    .replace("#*", ".");
};
```

### 将对象转换为数组

#### 方式 1

示例：{ userName : 'elite' , local : 'china' } 转换为 \[{ name : 'userName' , value : 'elite' } , { name : 'local' , value : 'china' }]

```javascript
const objTransToArray = function (obj) {
  let arr = [];
  Object.keys(obj).forEach((itemKey) => {
    let o = {};
    o.name = itemKey;
    o.value = obj[itemKey];
    arr.push(o);
  });
  return arr;
};
```

#### 方式 2

示例：{ userName : 'elite' , local : 'china' } 转换为 \[{ userName : 'elite' } , { local : 'china' }]

```javascript
const objTransToArray = function (obj) {
  let arr = [];
  Object.keys(obj).forEach((itemKey) => {
    let o = {};
    o[itemKey] = obj[itemKey];
    arr.push(o);
  });
  return arr;
};
```

### 判断数据类型

#### 方法 1 返回值为 Boolean 值

```javascript
// yourData 传入的数据
// dataType 要验证的数据类型，例如 String Number
const checkType = function (yourData, dataType) {
  const type = Object.prototype.toString.call(yourData).slice(8, -1);
  return type === dataType;
};
```

#### 方法 2 返回值为数据类型

```javascript
// checkdataType({}); // "object"
const checkdataType = function (yourData) {
  const type = Object.prototype.toString.call(yourData).slice(8, -1);
  return type.match(/\[object (.*?)\]/)[1].toLowerCase();
};
```

### 巧用 window\.open

不想使用 a 标签破环样式结构的话，可以使用 window\.open 实现部分常用功能\
`window.open(URL,name,specs,replace)`

```javascript
//  在当前页面显示拨打电话
window.open("tel:15000000000", "_self");
```

### js 获取 div 宽高

```javascript
const o = document.getElementById("view");
const height = o.offsetHeight;
const width = o.offsetWidth;
```

### js 时间处理

1. 方法 1：\
   传入一个合法的 date 格式时间数据，返回 {YY, MM, DD, HH, mm, ss}，默认返回当前时间的对象格式\
   例如：`formatDate(new Date())`返回对象：{ YY: '2021', MM: '01', DD: '12', HH: '09', mm: '56', ss: '55' }

```javascript
const formatDate = function (date = new Date()) {
  const YY = date.getFullYear() + "";
  const MM = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "";
  const DD = date.getDate() < 10 ? "0" + date.getDate() : date.getDate() + "";
  const HH = date.getHours() < 10 ? "0" + date.getHours() : date.getHours() + "";
  const mm = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes() + "";
  const ss = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds() + "";
  return { YY, MM, DD, HH, mm, ss };
};
```

2. 方法 2：\
   返回类似 "2021/01/12 09:48:04" 格式的时间字符串

```javascript
const formatNumber = (n) => {
  n = n.toString();
  return n[1] ? n : `0${n}`;
};
const formatTime = (date) => {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const hour = date.getHours();
  const minute = date.getMinutes();
  const second = date.getSeconds();

  return `${[year, month, day].map(formatNumber).join("/")} ${[hour, minute, second].map(formatNumber).join(":")}`;
};
```

### js 获取前一天的日期

```javascript
const yestodayDate = function () {
  const today = new Date();
  const yestoday = today - 1000 * 60 * 60 * 24;
  const yestodayDate = new Date(yestoday);
  const res = yestodayDate.toLocaleDateString().replace(/\//g, "-");
  return res;
};
```

### js 获取某月最后一天是几号

```javascript
const getLastdayOfTheMonth = function (data) {
  let month = new Date(date).getMonth() + 1;
  let year = new Date(date).getFullYear();
  if (month > 11) {
    month -= 12;
    year++;
  }
  let nextDate = new Date(year, month, 1);
  return new Date(nextDate.getTime() - 1000 * 60 * 60 * 24).getDate();
};
```

### 更新对象属性值

在不给目标对象增加新的属性的同时更新目标对象属性值

```javascript
const updateObj = function (target, source) {
  const keys = Object.keys(target);
  // const merge = Object.assign(target, source) 此方法会直接改变 target 对象
  const merge = { ...target, ...source };
  const temp = {};
  keys.map((item, index) => {
    temp[item] = merge[item];
  });
  return temp;
};
```

### 删除数组某一项

index 从 0 开始

```javascript
const deleteItem = function (index, arr) {
  arr.splice(index, 1);
};
```

### 获取文件类型

```javascript
//  方法1
const fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
//  方法2
const fileType = fileName.split(".").pop().toLowerCase();
```

### 复制文字

```javascript
function copy(targetValue) {
  let transfer = document.createElement("input");
  document.body.appendChild(transfer);
  transfer.value = targetValue; // 这里表示想要复制的内容
  transfer.focus();
  transfer.select();
  if (document.execCommand("copy")) {
    document.execCommand("copy");
  }
  transfer.blur();
  document.body.removeChild(transfer);
}
```

### 数组去重

```javascript
function uniqueArr(arr) {
  return Array.from(new Set(arr));
}

let tempArr = [1, 1, 2, 3, "3", 4, "4", 5, 5, 5, 6];
const resArr = uniqueArr(tempArr);
console.log(resArr); // [1, 2, 3, "3", 4, "4", 5, 6]
```

### 前端排序

1. 简单排序

```js
let students = [
  { name: "安吉利", score: 85, id: "324" },
  { name: "Ade", score: 92, id: "154" },
  { name: "aww", score: 78, id: "1666" },
  { name: "维尔得", score: 92, id: "12" },
];

// 1. 按分数升序排序
students.sort(function (a, b) {
  return a.score - b.score;
});
console.log("按分数升序:", students);

// 2. 按分数降序排序
students.sort(function (a, b) {
  return b.score - a.score;
});
console.log("按分数降序:", students);

// 3. 倒序整个数组
students.reverse();
console.log("倒序:", students);

// 4. 按姓名升序排序 (简单字符串比较)
students.sort(function (a, b) {
  if (a.name < b.name) {
    return -1;
  }
  if (a.name > b.name) {
    return 1;
  }
  return 0;
});
console.log("按姓名升序:", students);
```

2. 数字中英混合排序(简单)

* 代码说明

1. **使用 `localeCompare` 方法**：
   * `localeCompare` 是 JavaScript 内置的字符串比较方法
   * 参数 `'zh-CN'` 指定使用中文（简体）的排序规则
   * 自动处理中文字符的拼音排序（按拼音首字母顺序）
2. **排序规则**：
   * 中文按拼音首字母排序（如：陈(C) < 李(L) < 王(W) < 张(Z)）
   * 英文按字母顺序排序（A-Z）
   * 中英混合时统一按拼音/字母顺序混合排序（如：Alice < Bob < 陈六 < 李四）

* 注意事项：

1. **不改变原数组**：如果需要保留原数组，可先创建副本：

   ```javascript
   const sorted = [...arr].sort((a,b) => ...);
   ```
2. **浏览器兼容性**：
   * 所有现代浏览器均支持
   * IE10+ 支持（需要启用 Intl 支持）
3. **扩展功能**：

   * 如需区分大小写：添加 `sensitivity: 'case'` 选项
   * 如需数字智能排序：添加 `numeric: true` 选项

   ```javascript
   a.name.localeCompare(b.name, "zh-CN", {
     sensitivity: "case",
     numeric: true,
   });
   ```

```js
function sortByName(arr) {
  return arr.sort((a, b) => {
    return a.name.localeCompare(b.name, "zh-CN");
  });
}

// 示例用法
const arrayA = [
  { name: "1" },
  { name: "1123" },
  { name: "表格导入商标状态待审中1" },
  { name: "1A123" },
  { name: "测试权重2" },
  { name: "1a" },
  { name: "www测" },
  { name: "NIAOREN" },
  { name: "Alice" },
  { name: "Bob" },
  { name: "13网银" },
  { name: "3wang英吉利" },
  { name: "陈六" },
  { name: "bob" },
];

const sortedArray = sortByName(arrayA);
console.log(sortedArray);
// 注意！！！不同环境的js打印出的结果不一样，我测试的就有如下两种结果

/* 结果1：
[
  { name: '1' },
  { name: '1123' },
  { name: '13网银' },
  { name: '1a' },
  { name: '1A123' },
  { name: '3wang英吉利' },
  { name: 'Alice' },
  { name: 'bob' },
  { name: 'Bob' },
  { name: 'NIAOREN' },
  { name: 'www测' },
  { name: '测试权重2' },
  { name: '表格导入商标状态待审中1' },
  { name: '陈六' }
]
*/

/* 结果2：
[
  { name: '1' },
  { name: '1123' },
  { name: '13网银' },
  { name: '1a' },
  { name: '1A123' },
  { name: '3wang英吉利' },
  { name: '测试权重2' },
  { name: '表格导入商标状态待审中1' },
  { name: '陈六' }
  { name: 'Alice' },
  { name: 'bob' },
  { name: 'Bob' },
  { name: 'NIAOREN' },
  { name: 'www测' },
]
*/
```

3. 数字中英混合排序(严谨)\
   以下是实现按照数字 > 字母 > 中文的优先级进行排序的 JavaScript 代码：

```javascript
function sortByName(arr) {
  return arr.sort((a, b) => {
    const nameA = a.name || "";
    const nameB = b.name || "";

    // 判断字符类型
    const getCharType = (char) => {
      if (!char) return 3; // 空值处理
      if (/[\d]/.test(char)) return 0; // 数字
      if (/[a-zA-Z]/.test(char)) return 1; // 字母
      return 2; // 中文及其他
    };

    // 获取第一个有效字符的类型
    const firstCharA = nameA.charAt(0);
    const firstCharB = nameB.charAt(0);
    const typeA = getCharType(firstCharA);
    const typeB = getCharType(firstCharB);

    // 不同类型按优先级排序
    if (typeA !== typeB) {
      return typeA - typeB;
    }

    // 相同类型使用localeCompare排序
    return nameA.localeCompare(nameB, "zh-CN");
  });
}

// 测试示例
const testData = [
  { name: "张三" },
  { name: "李四" },
  { name: "apple" },
  { name: "Banana" },
  { name: "王五" },
  { name: "123" },
  { name: "42" },
  { name: "赵六" },
  { name: "zebra" },
  { name: "apple" },
  { name: "陈七" },
  { name: "" }, // 测试空字符串
  { name: "中文" },
  { name: "Alphabet" },
];

const sortedData = sortByName(testData);
console.log(sortedData);
```

* 代码说明：

1. **优先级规则**：

* **数字开头**（0-9）优先于所有其他类型
* **字母开头**（a-z，A-Z）次优先
* **中文/其他字符**最后

2. **实现逻辑**：

* **字符类型检测**：使用正则表达式判断首字符类型
* **优先级排序**：不同类型按数字(0) > 字母(1) > 中文(2)的顺序排序
* **同类型排序**：
  * 数字：按数值大小排序（如"42" > "123"）
  * 字母：按字母顺序排序（不区分大小写）
  * 中文：按拼音首字母排序

3. **特殊处理**：

* 空字符串或 undefined 视为中文类型
* 支持中英文混合字符串（如"中文 abc"）
* 正确处理大小写字母（A-Z 排在 a-z 之前）
* 测试输出结果：

```javascript
[
  { name: "123" }, // 数字
  { name: "42" }, // 数字
  { name: "Alphabet" }, // 字母
  { name: "apple" }, // 字母
  { name: "apple" }, // 字母
  { name: "Banana" }, // 字母
  { name: "zebra" }, // 字母
  { name: "" }, // 空字符串(视为中文)
  { name: "陈七" }, // 中文
  { name: "李四" }, // 中文
  { name: "王五" }, // 中文
  { name: "张三" }, // 中文
  { name: "赵六" }, // 中文
  { name: "中文" }, // 中文
];
```

* 关键特点：

1. **多级排序**：优先按首字符类型排序，再按具体值排序
2. **中文支持**：使用`localeCompare`的'zh-CN'参数实现拼音排序
3. **健壮性**：处理了空值、空字符串等边界情况
4. **效率优化**：仅对首字符进行类型检测，减少计算量

此实现严格遵循数字 > 字母 > 中文的优先级规则，同时确保同类型内的合理排序，满足各种实际应用场景的需求。

### 数组取交集/差集

```javascript
// 取交集
function intersection(a, b) {
  return a.filter((v) => b.includes(v));
}
// 取差集
function difference(a, b) {
  return a.concat(b).filter((v) => !a.includes(v) || !b.includes(v));
}
const a = [1, 2, 3, 4, 5];
const b = [1, 2, 3, 7, 8, 9];
intersection(a, b); // [1,2,3]
difference(a, b); // [4,5,7,8,9]
```

### 数组 a 是否包含数组 b

```javascript
arrayIncludes(a,b){
  return b.every(val=>a.includes(val));
}
```

### 实现按照给定 ID 数组顺序对对象数组进行排序

```js
function sortByIds(A, B) {
  // 创建映射：ID -> 在A中的索引
  const idIndexMap = new Map();
  A.forEach((id, index) => {
    idIndexMap.set(id, index);
  });

  // 创建新数组避免修改原数组，同时记录原始索引
  const sortable = B.map((item, originalIndex) => ({
    item,
    originalIndex,
    // 计算排序索引：如果ID在A中存在则使用A中的索引，否则排在最后并保持相对顺序
    sortIndex: idIndexMap.has(item.id) ? idIndexMap.get(item.id) : A.length + originalIndex,
  }));

  // 按排序索引进行排序
  sortable.sort((a, b) => a.sortIndex - b.sortIndex);

  // 返回排序后的对象数组
  return sortable.map((entry) => entry.item);
}

// 使用示例
const A = [3, 1, 2];
const B = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Charlie" },
  { id: 4, name: "David" }, // 不在A中的ID
];

const sortedB = sortByIds(A, B);
console.log(sortedB);
/* 输出：
[
    {id: 3, name: 'Charlie'},  // 对应A[0]
    {id: 1, name: 'Alice'},    // 对应A[1]
    {id: 2, name: 'Bob'},      // 对应A[2]
    {id: 4, name: 'David'}     // 不在A中的元素保持原相对顺序
]
*/
```

### 字符串转数组

```js
const str = "1, 2, 3, 4, 5";

const arr1 = str.split(",");
// 去除空格
const arr2 = str.split(",").map((item) => item.trim());
```

### 下划线转驼峰命名

```javascript
function tuHump(name) {
  return name.toLowerCase().replace(/\_(\w)/g, function (all, letter) {
    return letter.toUpperCase();
  });
}
```

### 滚动到页面顶部、底部

```js
// 返回顶部
const scrollToTop = () => {
  const targetDom = document.getElementById("scroll-wrap");
  if (targetDom) {
    targetDom.scrollTop = 0;
  }
};

// 返回底部
const scrollToBottom = () => {
  const targetDom = document.getElementById("scroll-wrap");
  targetDom.scrollTop = targetDom.scrollHeight;
};
const scrollToBottom = () => {
  const targetDom = document.getElementsByClassName("scroll-wrap");
  for (let i = 0; i < targetDom.length; i++) {
    targetDom[i].scrollTop = targetDom[i].scrollHeight;
  }
};
```

### 滚动到某个子元素的位置

```html
<div id="parent">
  <div class="child" id="child1">1</div>
  <div class="child" id="child2">2</div>
  <!-- ...... -->
  <div class="child" id="child9">9</div>
  <div class="child" id="child10">10</div>
</div>
<script>
  const parent = document.getElementById("parent");
  const child = document.getElementById("child4");
  parent.scrollTo({
    top: child.offsetTop, //需要父元素设置postion(relative、absolute、fixed)
    behavior: "smooth",
  });
</script>
```

### 获取两个元素的纵向距离

```js
let ele1Top = document.getElementById("ele1")?.getBoundingClientRect().top;
let ele2Top = document.getElementById("ele2")?.getBoundingClientRect().top;
const distance = ele2Top - ele1Top;
```

### 防抖和节流

函数防抖（debounce）：\
当持续触发事件时，一定时间段内没有再触发事件，事件处理函数才会执行一次，如果设定的时间到来之前，又一次触发了事件，就重新 开始延时。

```js
// 防抖函数
function debounce(func, wait) {
  let timeout = null;
  return function () {
    if (timeout !== null) clearTimeout(timeout);
    timeout = setTimeout(func, wait);
  };
}
// 处理函数
function handle() {
  console.log(Math.random());
}
// 滚动事件
window.addEventListener("scroll", debounce(handle, 1000));
```

函数节流（throttle）：\
当持续触发事件时，保证一定时间段内只调用一次事件处理函数。

```js
// 使用时间戳实现节流
function throttle(func, delay) {
  let prev = Date.now();
  return function () {
    let context = this;
    let args = arguments;
    let now = Date.now();
    if (now - prev >= delay) {
      func.apply(context, args);
      prev = Date.now();
    }
  };
}
function handle() {
  console.log(Math.random());
}
// 快速点击浏览器进行测试
window.addEventListener("click", throttle(handle, 1000));

// 使用定时器实现节流
function throttle(func, delay) {
  let timer = null;
  return function () {
    let context = this;
    let args = arguments;
    if (!timer) {
      timer = setTimeout(function () {
        func.apply(context, args);
        timer = null;
      }, delay);
    }
  };
}
function handle() {
  console.log(Math.random());
}
// 快速点击浏览器进行测试
window.addEventListener("click", throttle(handle, 1000));
```

### 判断一个字符串能否转换为有效的数字类型

```js
function isNumeric(str) {
  return typeof str === "string" && str.trim() !== "" && !isNaN(str);
}
```

### 压缩下载图片

```javascript
import JSZip from "jszip";
import { saveAs } from "file-saver";

async function downloadImages(imageUrls, zipFileName = "images.zip") {
  const zip = new JSZip();
  const imgFolder = zip.folder("images"); // 创建一个名为 'images' 的文件夹

  // 异步获取并添加图片到压缩包
  const imagePromises = imageUrls.map(async (imageUrl, index) => {
    try {
      const response = await fetch(imageUrl);
      const blob = await response.blob();
      const filename = imageUrl.substring(imageUrl.lastIndexOf("/") + 1) || `image-${index + 1}.jpg`;
      imgFolder.file(filename, blob);
    } catch (error) {
      console.error(`Error downloading or adding image ${imageUrl}:`, error);
      // 可以选择添加错误处理逻辑，比如跳过，或者提示用户
    }
  });

  // 等待所有图片都处理完毕
  await Promise.all(imagePromises);

  // 生成zip文件
  zip.generateAsync({ type: "blob" }).then(function (content) {
    saveAs(content, zipFileName);
  });
}

// 示例用法
const imageUrls = [
  "https://example.com/image1.jpg",
  "https://example.com/image2.png",
  "https://example.com/image3.gif",
];

downloadImages(imageUrls, "my_images.zip");
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://fan552426811.gitbook.io/front-end-tools/cssjs/common-js.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
