查漏补缺

106 阅读13分钟

查漏补缺

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

image.png

image.png

  
  <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

image.png

image.png

  
  //基础 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()

显示到浏览器上 image.png

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

image.png

image.png

14.debugger

image.png

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...inObject.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...inObject.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 是通过原型链来判断的。具体过程如下:

  1. 获取 object__proto__(即原型对象)。
  2. 检查 object.__proto__ 是否等于 constructor.prototype
  3. 如果不相等,继续沿着原型链向上查找(即 object.__proto__.__proto__)。
  4. 如果找到 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.prototypeAnimal.prototypeObject.prototypenull
  • 因此,dogDogAnimalObject 的实例。

2. 注意事项

  1. 原始值instanceof 只能用于对象,不能用于原始值(如 stringnumberboolean)。
 console.log("hello" instanceof String); // 输出: false
          console.log(123 instanceof Number); // 输出: false
  • 原始值不是对象,因此 instanceof 返回 false
  1. 跨框架对象:如果对象来自不同的框架(如 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
  1. 手动修改原型链:如果手动修改了对象的原型链,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 引擎通过以下两种主要策略判断对象是否为“垃圾”:

  1. 不再被引用:如果对象无法通过任何变量、属性或作用域链访问,则视为可回收。
  2. 不可达性:从全局对象(如 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) (主流算法)
  • 步骤

    1. 标记阶段:从根对象(全局变量、当前执行栈)出发,标记所有可达对象。
    2. 清除阶段:遍历堆内存,回收未被标记的对象。
  • 优点:解决了循环引用问题。

  • 缺点:可能产生内存碎片。

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 是什么?

clientWidthJavaScript 获取元素可见宽度 的一个属性,适用于 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

⚠️ 不包括 borderscrollbar


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,不包含 borderscrollbar
📌 用于获取真实可见宽度,常用于判断内容是否溢出

你是遇到 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)

问题就来了:

  1. filter() 返回的是一个数组,即使只有一个匹配的元素,它仍然是 [{ id: record.id, checked: false }] 这样的数组。
  2. 你不能直接修改 meet.checked,因为 meet 是数组,而 meet.checkedundefined,会报错。

❌ 错误写法


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](倒数第二个到末尾)