查漏补缺
1.制作表格 边框合并
border-collapse: collapse;//边框合并
<style>
table{
border-collapse: collapse;
}
table,
th,
td{
border: 1px solid black;
}
</style>
</head>
<body>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
</tr>
<tr>
<td id="nameCell"></td>
<td id="ageCell"></td>
<td id="sexCell"></td>
</tr>
</table>
2.-1/0、0/0、1/0
console.log(1 / -0); // -Infinity
console.log(-1 / -0); // Infinity
console.log(0 / -0); // NaN
3.reduce()
用于累加(归并)数组中的值,可以实现累加、扁平化、分组、统计等功能。
//示例 1:数组求和
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 15
//示例 2:数组最大值
const arr = [10, 20, 5, 40, 30];
const max = arr.reduce((acc, cur) => (cur > acc ? cur : acc), -Infinity);
//-Infinity 作为 initialValue(初始值)传递给 reduce() acc初始等于-Infinity
console.log(max); // 40
//示例 3:统计元素出现次数
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const count = fruits.reduce((acc, cur) => {
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
console.log(count);
// { apple: 3, banana: 2, orange: 1 }
//示例 4:数组对象分组
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 },
{ name: 'David', age: 30 }
];
const grouped = people.reduce((acc, person) => {
if (!acc[person.age]) {
acc[person.age] = [];
}
acc[person.age].push(person);
return acc;
}, {});
console.log(grouped);
/*
{
25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
30: [{ name: 'Bob', age: 30 }, { name: 'David', age: 30 }]
}
*/
// 示例 5:数组扁平化(flat)
const nestedArray = [[1, 2], [3, 4], [5, 6]];
const flatArray = nestedArray.reduce((acc, cur) => acc.concat(cur), []);
console.log(flatArray); // [1, 2, 3, 4, 5, 6]
//实现 map()
const numbers = [1, 2, 3, 4];
const squared = numbers.reduce((acc, cur) => {
acc.push(cur * cur);
return acc;
}, []);
console.log(squared); // [1, 4, 9, 16]
4.ASCII表格
5. inline-block和block区别
6.Map的使用
6.1用 Map 去重
7.concat()
concat()是 JavaScript 数组的一个方法,用于合并两个或多个数组,并返回一个新的数组。
//合并两个数组
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const result = arr1.concat(arr2);
console.log(result); // [1, 2, 3, 4, 5, 6]
console.log(arr1); // [1, 2, 3](未修改)
console.log(arr2); // [4, 5, 6](未修改)
//合并多个数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [5, 6];
const result = arr1.concat(arr2, arr3);
console.log(result); // [1, 2, 3, 4, 5, 6]
//合并值和数组
const arr = [1, 2, 3];
const result = arr.concat(4, [5, 6], 7);
console.log(result); // [1, 2, 3, 4, 5, 6, 7]
// 嵌套数组
const arr1 = [1, 2];
const arr2 = [[3, 4]];
const result = arr1.concat(arr2);
console.log(result); // [1, 2, [3, 4]]
//与 spread 语法的区别
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const result = [...arr1, ...arr2];
console.log(result); // [1, 2, 3, 4, 5, 6]
8.flliter()
//基本用法
const arr = [5, 12, 8, 130, 44];
const result = arr.filter(item => item > 10);
console.log(result); // [12, 130, 44]
///使用 index 参数
const arr = [10, 20, 30, 40, 50];
const evenIndex = arr.filter((item, index) => index % 2 === 0);
console.log(evenIndex); // [10, 30, 50]
//使用 thisArg
const obj = {
min: 10,
max: 40
};
const arr = [5, 12, 8, 130, 44];
const result = arr.filter(function (item) {
return item >= this.min && item <= this.max;
}, obj);
console.log(result); // [12]
//去除 null、undefined 和 NaN
const arr = [1, null, 2, undefined, 3, NaN, 4];
const cleanArr = arr.filter(item => item != null && !Number.isNaN(item));
console.log(cleanArr); // [1, 2, 3, 4]
9.box-shadow
<div class="box">Hover 阴影</div>
<style>
.box {
width: 200px;
height: 100px;
background-color: #ffffff;
transition: box-shadow 0.3s ease;
}
.box:hover {
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
</style>
10.transition
//基础 transition 示例
<div class="box">悬停试试</div>
<style>
.box {
width: 100px;
height: 50px;
background-color: #3498db;
color: #fff;
text-align: center;
line-height: 50px;
border-radius: 5px;
/* 定义 transition */
transition: background-color 0.5s ease, width 0.5s ease;
}
.box:hover {
background-color: #e74c3c;
width: 150px;
}
</style>
//多属性 transition
<style>
.box {
width: 100px;
height: 100px;
background-color: #1abc9c;
border-radius: 0;
transition: all 0.4s ease-in-out;
}
.box:hover {
width: 150px;
height: 150px;
background-color: #f39c12;
border-radius: 20px;
}
</style>
//
11.console.dir()
显示到浏览器上
12.cursor: pointer;
效果:鼠标悬停在灰色区域时变为手型,点击弹出提示框。
<!DOCTYPE html>
<html>
<head>
<style>
.clickable-div {
width: 200px;
padding: 20px;
background: #f0f0f0;
text-align: center;
cursor: pointer; /* 关键代码:手型光标 */
}
.clickable-div:hover {
background: #e0e0e0;
}
</style>
</head>
<body>
<div class="clickable-div" onclick="alert('点击生效!')">点击这个区域</div>
</body>
</html>
13.抛出异常
用throw
14.debugger
15.Symbol()
Symbol 是 JavaScript 中的一种原始数据类型,于 ES6(ECMAScript 2015)引入。它的主要特点是 唯一性 和 不可变性,通常用于创建唯一的标识符,避免属性名冲突。
1. 基本用法
通过 Symbol() 函数可以创建一个唯一的 Symbol 值:
const symbol1 = Symbol();
const symbol2 = Symbol();
console.log(symbol1 === symbol2); // 输出: false
- 每次调用
Symbol()都会生成一个全新的、唯一的值。 - Symbol 值不是字符串,而是一种特殊的原始类型。
2. Symbol 的描述符
可以为 Symbol 添加一个可选的描述符(字符串),用于调试或标识 Symbol 的用途:
const symbol = Symbol("mySymbol");
console.log(symbol.toString()); // 输出: Symbol(mySymbol)
- 描述符不会影响 Symbol 的唯一性,仅用于描述。
3. Symbol 的唯一性
Symbol 的主要用途是创建唯一的属性键,避免对象属性名冲突:
const obj = {};
const key1 = Symbol("key");
const key2 = Symbol("key");
obj[key1] = "value1";
obj[key2] = "value2";
console.log(obj[key1]); // 输出: value1
console.log(obj[key2]); // 输出: value2
- 即使两个 Symbol 的描述符相同,它们也是不同的值。
4. Symbol 作为对象属性
Symbol 可以作为对象的属性名,且不会被常规方法(如 for...in 或 Object.keys())枚举:
const obj = {
[Symbol("key1")]: "value1",
key2: "value2"
};
console.log(Object.keys(obj)); // 输出: ["key2"]
console.log(Object.getOwnPropertySymbols(obj)); // 输出: [Symbol(key1)]
- 使用
Object.getOwnPropertySymbols()可以获取对象的所有 Symbol 属性。
5. 全局 Symbol 注册表
通过 Symbol.for() 可以在全局 Symbol 注册表中创建或获取 Symbol。如果描述符相同,则返回同一个 Symbol:
const symbol1 = Symbol.for("mySymbol");
const symbol2 = Symbol.for("mySymbol");
console.log(symbol1 === symbol2); // 输出: true
- 使用
Symbol.keyFor()可以获取全局 Symbol 的描述符:
console.log(Symbol.keyFor(symbol1)); // 输出: mySymbol
6. 内置 Symbol 值
ES6 提供了一些内置的 Symbol 值,用于定义对象的默认行为。例如:
Symbol.iterator:定义对象的默认迭代器。Symbol.toStringTag:定义对象的toString()行为。Symbol.hasInstance:定义instanceof的行为。
示例:自定义迭代器
const myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
};
for (const value of myIterable) {
console.log(value); // 输出: 1, 2, 3
}
7. Symbol 的特性总结
| 特性 | 说明 |
|---|---|
| 唯一性 | 每个 Symbol 值都是唯一的,即使描述符相同。 |
| 不可变性 | Symbol 值创建后不可修改。 |
| 不可枚举 | Symbol 属性不会被 for...in 或 Object.keys() 枚举。 |
| 全局注册表 | 通过 Symbol.for() 可以在全局注册表中共享 Symbol。 |
| 内置 Symbol 值 | 用于定义对象的默认行为(如迭代、类型转换等)。 |
8. 使用场景
- 避免属性名冲突:在扩展对象时,使用 Symbol 作为属性名可以避免与现有属性冲突。
- 定义私有属性:Symbol 属性不会被常规方法枚举,可以模拟私有属性。
- 自定义对象行为:通过内置 Symbol 值(如
Symbol.iterator)定义对象的默认行为。
示例:避免属性名冲突
const user = {
name: "Alice",
age: 25
};
// 扩展对象时使用 Symbol 作为属性名
const id = Symbol("id");
user[id] = 123;
console.log(user); // 输出: { name: "Alice", age: 25, [Symbol(id)]: 123 }
16.instanceof
1. 原型链的作用
instanceof 是通过原型链来判断的。具体过程如下:
- 获取
object的__proto__(即原型对象)。 - 检查
object.__proto__是否等于constructor.prototype。 - 如果不相等,继续沿着原型链向上查找(即
object.__proto__.__proto__)。 - 如果找到
constructor.prototype,返回true;如果原型链的尽头(null)仍未找到,返回false。
示例:原型链
function Animal() {}
function Dog() {}
// 设置 Dog 的原型为 Animal 的实例
Dog.prototype = new Animal();
const dog = new Dog();
console.log(dog instanceof Dog); // 输出: true
console.log(dog instanceof Animal); // 输出: true
console.log(dog instanceof Object); // 输出: true
dog的原型链:Dog.prototype→Animal.prototype→Object.prototype→null。- 因此,
dog是Dog、Animal和Object的实例。
2. 注意事项
- 原始值:
instanceof只能用于对象,不能用于原始值(如string、number、boolean)。
console.log("hello" instanceof String); // 输出: false
console.log(123 instanceof Number); // 输出: false
- 原始值不是对象,因此
instanceof返回false。
- 跨框架对象:如果对象来自不同的框架(如
iframe),instanceof可能失效,因为不同框架的全局环境是隔离的。
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
const iframeArray = iframe.contentWindow.Array;
const arr = new iframeArray();
console.log(arr instanceof Array); // 输出: false
console.log(arr instanceof iframeArray); // 输出: true
- 手动修改原型链:如果手动修改了对象的原型链,
instanceof的结果可能会受到影响。
function A() {}
function B() {}
const a = new A();
console.log(a instanceof A); // 输出: true
// 修改原型链
Object.setPrototypeOf(a, B.prototype);
console.log(a instanceof A); // 输出: false
console.log(a instanceof B); // 输出: true
3. 与 typeof 的区别
- typeof:用于检查值的类型(如
"string"、"number"、"object"等)。 - instanceof:用于检查对象是否是某个构造函数的实例。
示例:
const arr = [1, 2, 3];
console.log(typeof arr); // 输出: "object"
console.log(arr instanceof Array); // 输出: true
17.斐波那契数列
斐波那契数列(Fibonacci sequence)是一个经典的数学序列,其定义如下:
- 第 0 项为 0,第 1 项为 1。
- 从第 2 项开始,每一项等于前两项之和。
function fibonacci(n) {
if (n === 0) return 0;
if (n === 1) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
//打印数列
function printFibonacci(n) {
if (n <= 0) {
console.log("请输入一个大于 0 的整数。");
return;
}
let prev = 0, curr = 1;
let result = [prev]; // 初始化结果数组,包含第 0 项
for (let i = 1; i < n; i++) {
result.push(curr); // 将当前项加入结果数组
const next = prev + curr; // 计算下一项
prev = curr; // 更新前一项
curr = next; // 更新当前项
}
console.log(result.join(", ")); // 打印结果
}
// 示例:打印前 10 项斐波那契数列
printFibonacci(10);
18.Js垃圾回收机制
一、垃圾回收的基本原理
JavaScript 引擎通过以下两种主要策略判断对象是否为“垃圾”:
- 不再被引用:如果对象无法通过任何变量、属性或作用域链访问,则视为可回收。
- 不可达性:从全局对象(如
window或global)出发,无法遍历到的对象。
二、主要垃圾回收算法
1. 引用计数(Reference Counting) (已逐渐淘汰)
-
原理:记录每个对象被引用的次数,当引用次数变为 0 时回收。
-
缺点:无法处理循环引用(两个对象互相引用,但整体不可达)。
function createCycle() { let obj1 = {}; let obj2 = {}; obj1.ref = obj2; // obj1 引用 obj2 obj2.ref = obj1; // obj2 引用 obj1 } createCycle(); // 执行后,obj1 和 obj2 的引用次数仍为 1,无法回收
2. 标记-清除(Mark-and-Sweep) (主流算法)
-
步骤:
- 标记阶段:从根对象(全局变量、当前执行栈)出发,标记所有可达对象。
- 清除阶段:遍历堆内存,回收未被标记的对象。
-
优点:解决了循环引用问题。
-
缺点:可能产生内存碎片。
19.toUpperCase()
const a = 'sdiad'
console.log(a.toUpperCase())
20.filtter 不会改变原数组
会返回一个新数组
21. list.unshift()
往数组的前面添加新数据 而list.push()是往数组的后面添加数据
22. trim()
add () {
if (this.todoName.trim() === '') {
alert('请输入任务名称')
return
}
this.list.unshift({
id: +new Date(),
name: this.todoName
})
this.todoName = ''
}
23.setInterval和setTimeout有什么区别
1. 执行机制不同
setTimeout
→ 在指定的延迟时间后执行一次回调函数
→ 例如:setTimeout(() => console.log('执行'), 1000)会在 1 秒后输出一次setInterval
→ 重复执行回调函数,每次间隔固定的时间
→ 例如:setInterval(() => console.log('执行'), 1000)会每 1 秒输出一次,直到被清除
24.JSON.stringify JSON.parse
// 存储到localStorage
const user = { name: "Alice", id: 123 };
localStorage.setItem("user", JSON.stringify(user));
// 从localStorage读取
const storedUser = JSON.parse(localStorage.getItem("user"));
25.关于grid布局
grid-template-colums(列):repeat(), grid-area,grid-template-rows(行)
.z-shape {
display: grid;
/* TODO:2行4列 */
grid-area: span 2 / span 4;
}
.l-shape {
display: grid;
/* TODO:2行3列 */
grid-area: span 2 / span 3;
}
//第二:
.z-shape {
display: grid; /* TODO:2行4列 */
grid-template-columns: repeat(4,30px);
grid-template-rows: repeat(2,30px);
}
.l-shape {
display: grid; /* TODO:待补充代码 */
grid-template-columns: repeat(3,30px);
grid-template-rows: repeat(2,30px); }
//第三
.z-shape {
display: grid; /* TODO:待补充代码 */
grid-template-rows: 30px 30px;
grid-template-columns: 30px 30px 30px 30px; }
.l-shape {
display: grid;
/* TODO:待补充代码 */
grid-template-rows: 30px 30px;
grid-template-columns: 30px 30px 30px; }
26.flatMap()
👉 flatMap() 的作用
- 展开数组中的
Set集合,然后把每个Set转换成数组。
等价于:
[a, ...bSets].map(s => [...s]).flat()
class XSet extends Set {
static union(a, ...bSets) {
return new XSet([a, ...bSets].flatMap(s => [...s]));
}
}
let set1 = new Set([1, 2]);
let set2 = new Set([3, 4]);
let set3 = new Set([2, 5]);
let result = XSet.union(set1, set2, set3);
console.log(result); // XSet {1, 2, 3, 4, 5}
27.new XSet()
new XSet(...)
- 创建一个新的
XSet实例,去除重复元素(假设XSet继承Set)。 Set结构会自动去重,所以[1, 2, 3, 4, 2, 5]变成{1, 2, 3, 4, 5}。
28. clientWidth 是什么?
clientWidth 是 JavaScript 获取元素可见宽度 的一个属性,适用于 HTMLElement 对象。
1. clientWidth 计算规则
它返回一个元素的内部宽度,包含:
- 内容 (content)
- 内边距 (padding)
- ❌ 不包括边框 (border)
- ❌ 不包括滚动条 (scrollbar)
示例:
<div id="box" style="width: 300px; padding: 20px; border: 10px solid black; overflow: auto;">
这是一个测试盒子
</div>
<script>
let box = document.getElementById('box');
console.log(box.clientWidth); // 340 (300 + 20 + 20)
</script>
✅ 计算方式:
clientWidth = width + 左右 padding
⚠️ 不包括 border 和 scrollbar。
2. clientWidth vs. offsetWidth vs. scrollWidth
| 属性 | 是否包含 padding | 是否包含 border | 是否包含滚动条 | 是否包含超出部分 |
|---|---|---|---|---|
clientWidth | ✅ 是 | ❌ 否 | ❌ 否 | ❌ 否 |
offsetWidth | ✅ 是 | ✅ 是 | ✅ 是 | ❌ 否 |
scrollWidth | ✅ 是 | ❌ 否 | ❌ 否 | ✅ 是 |
对比示例
<div id="test" style="width: 200px; padding: 10px; border: 5px solid black; overflow: auto;">
很长的文本,很长的文本,很长的文本...
</div>
<script>
let test = document.getElementById('test');
console.log(test.clientWidth); // 220 (200 + 10 + 10)
console.log(test.offsetWidth); // 230 (200 + 10 + 10 + 5 + 5)
console.log(test.scrollWidth); // 可能大于 clientWidth (如果内容溢出)
</script>
3. clientWidth 常见用途
✅ 获取可视宽度(不包含滚动条)
console.log(document.documentElement.clientWidth); // 获取视口宽度
✅ 判断元素是否超出
if (element.scrollWidth > element.clientWidth) {
console.log("内容超出了容器");
}
✅ 响应式布局
window.addEventListener('resize', () => {
console.log(document.documentElement.clientWidth); // 动态监听宽度
});
4. 总结
📌 clientWidth 是元素的“内部宽度”
📌 包含 padding,不包含 border 和 scrollbar
📌 用于获取真实可见宽度,常用于判断内容是否溢出
你是遇到 clientWidth 的问题,还是想知道如何在实际项目中使用?😃
29.filter 和find
list.value.forEach(item => {
const meet = item.meetings.find(i => i.id === record.id) // 找到第一个匹配的 meeting
console.log(meet);
if (meet) { // 如果找到了
meet.checked = !meet.checked
item.checked = item.meetings.every(e => e.checked) // 判断所有 meetings 是否都被选中
}
});
为什么这里用 find() 而不是 filter()?
在你的代码中:
const meet = item.meetings.find(i => i.id === record.id)
这里使用 find(),而不是 filter(),是因为你只需要找到第一个匹配的元素,而不是所有匹配的元素。
如果用 filter() 会发生什么?
假设你改成:
const meet = item.meetings.filter(i => i.id === record.id)
问题就来了:
filter()返回的是一个数组,即使只有一个匹配的元素,它仍然是[{ id: record.id, checked: false }]这样的数组。- 你不能直接修改
meet.checked,因为meet是数组,而meet.checked是undefined,会报错。
❌ 错误写法
const meet = item.meetings.filter(i => i.id === record.id);
console.log(meet); // 输出一个数组,而不是单个对象
if (meet) { // ❌ 这里 meet 是数组,永远为真
meet.checked = !meet.checked; // ❌ 会报错:Cannot set properties of undefined
}
✅ 正确写法
如果一定要用 filter() ,你需要先取数组的第一个元素:
js
复制编辑
const meet = item.meetings.filter(i => i.id === record.id)[0]; // 取第一个元素
if (meet) {
meet.checked = !meet.checked;
}
但这样写不如 find() 直接简单。
30.@dbclick
双击
31.slice和splice
let arr = [1, 2, 3, 4, 5];
// 删除:从索引 1 开始删除 2 个元素
console.log(arr.splice(1, 2)); // 输出: [2, 3]
console.log(arr); // 原数组变为: [1, 4, 5]
// 添加:从索引 1 开始删除 0 个元素,插入 'a', 'b'
arr = [1, 4, 5];
console.log(arr.splice(1, 0, 'a', 'b')); // 输出: []
console.log(arr); // 变为: [1, 'a', 'b', 4, 5]
// 替换:从索引 2 开始删除 1 个元素,插入 'x'
arr = [1, 'a', 'b', 4, 5];
console.log(arr.splice(2, 1, 'x')); // 输出: ['b']
console.log(arr); // 变为: [1, 'a', 'x', 4, 5]
// 负索引示例
arr = [1, 2, 3, 4, 5];
console.log(arr.splice(-2, 1)); // 输出: [4](删除倒数第二个元素)
console.log(arr); // 变为: [1, 2, 3, 5]
const arr = [1, 2, 3, 4, 5];
// 提取索引 1 到 3(不包含)的元素
console.log(arr.slice(1, 3)); // 输出: [2, 3]
console.log(arr); // 原数组不变: [1, 2, 3, 4, 5]
// 负索引示例
console.log(arr.slice(-2)); // 输出: [4, 5](倒数第二个到末尾)