跳转到内容

JS相关知识4.0

JavaScript中setTimeout()方法

在 JavaScript 中,setTimeout() 是一个用于在指定时间后执行代码的全局函数。它允许你延迟执行一段代码或函数,通常用于实现定时任务、延迟操作或异步行为。

基本语法

javascript
setTimeout(callback, delay, arg1, arg2, ...);
  • callback:要执行的函数或代码块。
  • delay:延迟的时间(以毫秒为单位)。默认值为 0,表示尽快执行。
  • arg1, arg2, ...(可选):传递给回调函数的参数。

示例代码

  1. 基本用法

    javascript
    setTimeout(function() {
      console.log('Hello after 2 seconds');
    }, 2000); // 2 秒后执行
  2. 传递参数

    javascript
    setTimeout(function(name, age) {
      console.log(`Hello, ${name}! You are ${age} years old.`);
    }, 1000, 'Alice', 25); // 1 秒后执行,并传递参数
  3. 使用箭头函数

    javascript
    setTimeout(() => {
      console.log('This is an arrow function');
    }, 500); // 0.5 秒后执行
  4. 立即执行(delay 为 0)

    javascript
    setTimeout(function() {
      console.log('This runs as soon as possible');
    }, 0); // 尽快执行

返回值

  • setTimeout() 返回一个唯一的 定时器 ID(数字类型),可以用于后续取消定时器。
  • 使用 clearTimeout() 方法可以取消尚未执行的定时器。
javascript
let timerId = setTimeout(function() {
  console.log('This will not run');
}, 1000);

clearTimeout(timerId); // 取消定时器

注意事项

  1. 异步执行

    • setTimeout() 是异步的,即使延迟时间为 0,回调函数也会被放入事件队列,等待当前代码执行完毕后再执行。
    javascript
    console.log('Start');
    setTimeout(() => {
      console.log('Inside setTimeout');
    }, 0);
    console.log('End');
    
    // 输出顺序:
    // Start
    // End
    // Inside setTimeout
  2. this 绑定

    • 在非箭头函数中,setTimeout 回调函数中的 this 默认指向全局对象(浏览器中为 window,Node.js 中为 global)。如果需要绑定特定的 this,可以使用 bind 或箭头函数。
    javascript
    let obj = {
      name: 'Alice',
      greet: function() {
        setTimeout(function() {
          console.log(`Hello, ${this.name}`); // this 指向全局对象
        }, 1000);
      }
    };
    obj.greet(); // 输出: Hello, undefined
    
    // 使用 bind 绑定 this
    let obj2 = {
      name: 'Bob',
      greet: function() {
        setTimeout(function() {
          console.log(`Hello, ${this.name}`);
        }.bind(this), 1000);
      }
    };
    obj2.greet(); // 输出: Hello, Bob
    
    // 使用箭头函数
    let obj3 = {
      name: 'Charlie',
      greet: function() {
        setTimeout(() => {
          console.log(`Hello, ${this.name}`); // this 指向 obj3
        }, 1000);
      }
    };
    obj3.greet(); // 输出: Hello, Charlie
  3. 最小延迟时间

    • 浏览器中,setTimeout 的最小延迟时间通常为 4ms(即使设置为 0)。这是为了防止过度消耗资源。
  4. 嵌套 setTimeout

    • 可以使用嵌套的 setTimeout 来实现周期性任务(类似于 setInterval),但更灵活。
    javascript
    let counter = 0;
    function run() {
      console.log(`Counter: ${counter}`);
      counter++;
      if (counter < 5) {
        setTimeout(run, 1000); // 1 秒后再次执行
      }
    }
    run();

setInterval() 的区别

特性setTimeout()setInterval()
执行次数单次执行重复执行,直到被取消
取消方法clearTimeout()clearInterval()
适用场景延迟任务、一次性任务周期性任务(如轮询、动画)
灵活性可以通过嵌套实现周期性任务直接支持周期性任务

总结

  • setTimeout() 用于延迟执行代码。
  • 返回定时器 ID,可通过 clearTimeout() 取消。
  • 是异步的,即使延迟为 0 也会放入事件队列。
  • 适用于延迟任务、异步操作和周期性任务的实现。

JavaScript中setInterval()方法

setInterval() 是 JavaScript 中的一个全局方法,用于按照指定的时间间隔重复执行某个函数或代码片段。它通常用于需要周期性执行任务的场景,例如定时更新页面内容、轮播图、定时请求数据等。

语法

javascript
setInterval(func, delay, arg1, arg2, ...);
  • func: 要重复执行的函数或代码片段。
  • delay: 每次执行之间的时间间隔,以毫秒为单位(1000 毫秒 = 1 秒)。
  • arg1, arg2, ... (可选): 传递给 func 的额外参数。

返回值

setInterval() 返回一个唯一的间隔 ID(intervalID),这个 ID 可以用于后续清除间隔(停止重复执行)。

示例

javascript
// 每隔 1 秒打印一次 "Hello, World!"
const intervalID = setInterval(() => {
    console.log("Hello, World!");
}, 1000);

// 5 秒后停止打印
setTimeout(() => {
    clearInterval(intervalID);
    console.log("Interval stopped.");
}, 5000);

清除间隔

使用 clearInterval() 方法可以停止 setInterval() 的执行。你需要传递 setInterval() 返回的 intervalID 作为参数。

javascript
clearInterval(intervalID);

注意事项

  1. 时间间隔的准确性: setInterval() 并不能保证严格的时间间隔,因为 JavaScript 是单线程的,可能会受到其他任务的影响。
  2. 内存泄漏: 如果不及时清除不再需要的 setInterval(),可能会导致内存泄漏。
  3. 嵌套 setInterval(): 如果在一个 setInterval() 回调函数中再次调用 setInterval(),可能会导致多个间隔同时运行,需谨慎使用。

替代方案

在某些场景下,setTimeout() 可以替代 setInterval(),尤其是在需要动态调整间隔时间或确保前一个任务完成后再执行下一个任务时。

javascript
function repeatTask() {
    console.log("Task executed");
    setTimeout(repeatTask, 1000); // 1 秒后再次执行
}

repeatTask(); // 启动任务

通过这种方式,可以更好地控制任务的执行时机。

JavaScript清除定时器

在 JavaScript 中,定时器(Timer)用于延迟执行代码或周期性执行代码。常见的定时器包括 setTimeoutsetInterval。为了确保定时器不会在不需要时继续运行,需要手动清除定时器。以下是清除定时器的方法及示例:

1. 清除 setTimeout 定时器

setTimeout 用于延迟执行代码一次,返回一个定时器 ID。可以使用 clearTimeout 清除定时器。

示例:

javascript
// 设置定时器
const timerId = setTimeout(() => {
    console.log("定时器执行");
}, 1000);

// 清除定时器
clearTimeout(timerId);

2. 清除 setInterval 定时器

setInterval 用于周期性执行代码,返回一个定时器 ID。可以使用 clearInterval 清除定时器。

示例:

javascript
// 设置定时器
const intervalId = setInterval(() => {
    console.log("定时器执行");
}, 1000);

// 清除定时器
clearInterval(intervalId);

3. 清除所有定时器

如果需要清除所有定时器,可以将定时器 ID 存储在数组中,然后遍历清除。

示例:

javascript
const timerIds = [];

// 设置多个定时器
timerIds.push(setTimeout(() => console.log("定时器 1"), 1000));
timerIds.push(setTimeout(() => console.log("定时器 2"), 2000));

// 清除所有定时器
timerIds.forEach(id => clearTimeout(id));

4. 在组件或页面卸载时清除定时器

在单页面应用(SPA)或组件化开发中,确保在组件卸载时清除定时器,避免内存泄漏。

示例(React):

javascript
import React, { useEffect } from "react";

function MyComponent() {
    useEffect(() => {
        const timerId = setTimeout(() => {
            console.log("定时器执行");
        }, 1000);

        // 清除定时器
        return () => clearTimeout(timerId);
    }, []);

    return <div>组件内容</div>;
}

5. 使用 Promise 封装定时器

通过 Promise 封装定时器,可以更方便地控制定时器的执行和清除。

示例:

javascript
function delay(ms) {
    let timerId;
    const promise = new Promise((resolve) => {
        timerId = setTimeout(resolve, ms);
    });
    return { promise, timerId };
}

const { promise, timerId } = delay(1000);
promise.then(() => console.log("定时器执行"));

// 清除定时器
clearTimeout(timerId);

6. 使用 AbortController 控制定时器

AbortController 可以用于取消异步操作,结合 Promise 实现定时器的控制。

示例:

javascript
function delay(ms, signal) {
    return new Promise((resolve, reject) => {
        const timerId = setTimeout(resolve, ms);
        signal.addEventListener("abort", () => {
            clearTimeout(timerId);
            reject(new Error("定时器已取消"));
        });
    });
}

const controller = new AbortController();
delay(1000, controller.signal)
    .then(() => console.log("定时器执行"))
    .catch((err) => console.log(err.message));

// 取消定时器
controller.abort();

7. 注意事项

  • 定时器 ID 是唯一的:每次调用 setTimeoutsetInterval 都会返回一个新的 ID。
  • 清除无效定时器:如果定时器已经执行或清除,再次调用 clearTimeoutclearInterval 不会报错。
  • 内存泄漏:未清除的定时器可能导致内存泄漏,尤其是在单页面应用中。

总结

方法适用场景示例
clearTimeout清除setTimeout 定时器clearTimeout(timerId)
clearInterval清除setInterval 定时器clearInterval(intervalId)
存储定时器 ID清除多个定时器timerIds.forEach(clearTimeout)
组件卸载时清除避免内存泄漏React 的useEffect 清理函数
Promise 封装控制定时器执行delay(ms).then(...)
AbortController取消定时器controller.abort()

通过合理使用这些方法,可以有效管理 JavaScript 中的定时器,避免不必要的资源消耗和内存泄漏。

简述ECMAScript

ECMAScript(通常缩写为 ES)是 JavaScript 的标准化规范,由 ECMA International 组织制定和维护。JavaScript 是 ECMAScript 规范的一种实现,其他实现还包括 ActionScript(Adobe Flash 使用的脚本语言)等。ECMAScript 定义了脚本语言的核心特性,包括语法、类型、语句、关键字、操作符、对象等。

关键点

  1. 历史背景

    • JavaScript 最初由 Netscape 开发,后来微软也推出了 JScript。
    • 为了避免浏览器脚本语言的碎片化,Netscape 将 JavaScript 提交给 ECMA International 进行标准化,最终形成了 ECMAScript 标准。
  2. 版本演进

    • ES1 (1997):第一个正式版本,奠定了 JavaScript 的基础。
    • ES3 (1999):引入了正则表达式、异常处理(try/catch)等特性。
    • ES5 (2009):增加了严格模式("use strict")、JSON 支持、数组方法(如 mapfilter)等。
    • ES6 (2015, 也称为 ES2015):重大更新,引入了 let/const、箭头函数、类、模块化、Promise、解构赋值等现代 JavaScript 的核心特性。
    • ES2016 及以后:每年发布一个新版本,逐步增加新特性,如 async/await、可选链操作符(?.)、空值合并操作符(??)等。
  3. 核心特性

    • 动态类型:变量不需要声明类型,类型在运行时确定。
    • 基于原型:JavaScript 使用原型链实现继承,而不是传统的类继承(ES6 引入了 class 语法糖,但底层仍然是基于原型)。
    • 函数是一等公民:函数可以作为参数传递、返回值或赋值给变量。
    • 异步编程:通过回调函数、Promise 和 async/await 支持异步操作。
  4. 与 JavaScript 的关系

    • ECMAScript 是标准,JavaScript 是 ECMAScript 的实现。
    • 浏览器和 Node.js 等运行环境会根据 ECMAScript 标准实现 JavaScript 引擎(如 V8、SpiderMonkey 等)。
  5. 现代 JavaScript

    • 现代 JavaScript 开发通常基于 ES6 及更高版本,结合模块化、工具链(如 Babel、Webpack)和框架(如 React、Vue)构建复杂应用。
    • 新特性如箭头函数、模板字符串、解构赋值等极大地提升了开发效率和代码可读性。

示例代码

以下是 ES6 及更高版本的一些特性示例:

javascript
// 箭头函数
const add = (a, b) => a + b;

// 模板字符串
const name = "Alice";
console.log(`Hello, ${name}!`);

// 解构赋值
const [x, y] = [1, 2];
const { age, city } = { age: 25, city: "New York" };

// 类
class Person {
    constructor(name) {
        this.name = name;
    }
    greet() {
        console.log(`Hello, ${this.name}!`);
    }
}

// Promise 和 async/await
const fetchData = async () => {
    const response = await fetch("https://api.example.com/data");
    const data = await response.json();
    console.log(data);
};

总结

ECMAScript 是 JavaScript 的核心标准,定义了语言的基本特性和行为。随着版本的不断更新,JavaScript 变得越来越强大和易用,成为现代 Web 开发中不可或缺的一部分。

阐述JavaScript ES6

JavaScript ES6(ECMAScript 2015)是 JavaScript 语言的一次重大更新,引入了许多新特性,使代码更简洁、功能更强大。以下是 ES6 的主要特性及其应用场景:

1. 块级作用域与 letconst

(1) letconst

  • let:声明块级作用域的变量。
  • const:声明块级作用域的常量。

示例

javascript
let x = 10;
const y = 20;
if (true) {
  let x = 30;
  const y = 40;
  console.log(x, y); // 输出:30 40
}
console.log(x, y); // 输出:10 20

(2) 块级作用域

  • 使用 {} 定义块级作用域,避免变量污染。

2. 箭头函数(Arrow Functions)

  • 更简洁的函数语法。
  • 自动绑定 this(词法作用域)。

示例

javascript
const add = (a, b) => a + b;
console.log(add(2, 3)); // 输出:5

3. 模板字符串(Template Literals)

  • 使用反引号(`)定义字符串,支持多行文本和嵌入表达式。

示例

javascript
const name = 'John';
const message = `Hello, ${name}!
Welcome to ES6.`;
console.log(message);

4. 解构赋值(Destructuring Assignment)

  • 从数组或对象中提取值并赋值给变量。

示例

javascript
// 数组解构
const [a, b] = [1, 2];
console.log(a, b); // 输出:1 2

// 对象解构
const { name, age } = { name: 'John', age: 30 };
console.log(name, age); // 输出:John 30

5. 默认参数(Default Parameters)

  • 为函数参数设置默认值。

示例

javascript
function greet(name = 'Guest') {
  console.log(`Hello, ${name}`);
}
greet(); // 输出:Hello, Guest

6. 剩余参数与扩展运算符

(1) 剩余参数(Rest Parameters)

  • 将多个参数收集为一个数组。

示例

javascript
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 输出:6

(2) 扩展运算符(Spread Operator)

  • 将数组或对象展开。

示例

javascript
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // 输出:[1, 2, 3, 4, 5]

7. 类与继承(Classes)

  • 提供更清晰的面向对象编程语法。

示例

javascript
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks.`);
  }
}

const dog = new Dog('Rex');
dog.speak(); // 输出:Rex barks.

8. 模块化(Modules)

  • 使用 importexport 实现模块化。

示例

javascript
// math.js
export function add(a, b) {
  return a + b;
}

// main.js
import { add } from './math.js';
console.log(add(2, 3)); // 输出:5

9. Promise 与异步编程

  • 提供更优雅的异步编程方式。

示例

javascript
const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('Data fetched'), 1000);
  });
};

fetchData().then(data => console.log(data)); // 输出:Data fetched

10. 其他特性

(1) 对象字面量增强

  • 简写属性和方法。

示例

javascript
const name = 'John';
const obj = { name, greet() { console.log('Hello'); } };

(2) Symbol 类型

  • 创建唯一标识符。

示例

javascript
const id = Symbol('id');

(3) MapSet

  • 提供更强大的数据结构。

示例

javascript
const map = new Map();
map.set('key', 'value');
console.log(map.get('key')); // 输出:value

总结

ES6 引入了许多新特性,使 JavaScript 更现代化、功能更强大。以下是主要特性的对比:

特性描述示例
let/const块级作用域变量声明let x = 10;
箭头函数简洁的函数语法,自动绑定thisconst add = (a, b) => a + b;
模板字符串支持多行文本和嵌入表达式`Hello, ${name}`
解构赋值从数组或对象中提取值const [a, b] = [1, 2];
默认参数为函数参数设置默认值function greet(name = 'Guest')
剩余参数与扩展运算符收集参数或展开数组/对象const arr2 = [...arr1];
类与继承提供面向对象编程语法class Dog extends Animal
模块化使用importexportimport { add } from './math';
Promise提供更优雅的异步编程方式fetchData().then(data => ...)

掌握 ES6 特性,可以显著提升代码质量和开发效率。

手写防抖和节流,以及两者之间的区别

防抖(Debounce)和节流(Throttle)是两种常见的优化高频事件处理的技术。它们的主要目的是减少事件触发的频率,从而提高性能。

1. 防抖(Debounce)

防抖的核心思想是:在事件触发后,等待一段时间(例如 n 毫秒),如果在这段时间内没有再次触发事件,才执行处理函数。如果在等待时间内又触发了事件,则重新计时。

应用场景:输入框实时搜索、窗口大小调整等。

function debounce(func, wait) {
    let timeout;
    return function(...args) {
        const context = this;
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(context, args), wait);
    };
}

// 示例用法
const input = document.querySelector('input');
input.addEventListener('input', debounce(function() {
    console.log('Input value:', input.value);
}, 300));

2. 节流(Throttle)

节流的核心思想是:在一定时间间隔内(例如 n 毫秒),只执行一次处理函数。即使在这段时间内事件被多次触发,也只会执行一次。

应用场景:滚动事件、鼠标移动事件等。

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

// 示例用法
window.addEventListener('scroll', throttle(function() {
    console.log('Scrolling...');
}, 200));

3. 防抖和节流的区别

  • 防抖:在事件触发后,等待一段时间,如果在这段时间内没有再次触发事件,才执行处理函数。如果在等待时间内又触发了事件,则重新计时。
  • 节流:在一定时间间隔内,只执行一次处理函数。即使在这段时间内事件被多次触发,也只会执行一次。

总结

  • 防抖:适合处理连续触发的事件,但只关心最后一次触发的结果(如输入框实时搜索)。
  • 节流:适合处理连续触发的事件,但希望在一定时间间隔内只执行一次(如滚动事件)。

通过合理使用防抖和节流,可以有效地优化高频事件的处理,提升用户体验和性能。

attribute和property的区别

在 Web 开发中,attributeproperty 是两个容易混淆的概念,它们都与 HTML 元素的特性相关,但它们的来源、行为和用途有显著区别。

1. 核心区别

特性Attribute(属性)Property(特性)
定义HTML 标签上的特性(写在 HTML 中)DOM 对象上的特性(通过 JavaScript 访问)
来源HTML 标签DOM 对象
数据类型始终是字符串可以是任意类型(字符串、布尔值、数字等)
同步性初始同步,后续可能不同步动态更新,可能影响 attribute
示例<input type="text" value="Hello">input.value = "World"

2. 详细对比

(1) Attribute

  • 定义:HTML 标签上的特性,写在 HTML 中。
  • 访问方式
    • JavaScript:element.getAttribute()element.setAttribute()
    • HTML:直接在标签中定义。
  • 数据类型:始终是字符串。
  • 同步性:初始时与 property 同步,后续可能不同步。

示例

html
<input id="input" type="text" value="Hello">
javascript
const input = document.getElementById('input');
console.log(input.getAttribute('value')); // 输出:Hello

(2) Property

  • 定义:DOM 对象上的特性,通过 JavaScript 访问。
  • 访问方式:直接通过 DOM 对象访问。
  • 数据类型:可以是任意类型(如布尔值、数字、对象等)。
  • 同步性:动态更新,可能影响 attribute。

示例

javascript
const input = document.getElementById('input');
console.log(input.value); // 输出:Hello
input.value = 'World'; // 修改 property
console.log(input.value); // 输出:World
console.log(input.getAttribute('value')); // 输出:Hello(未同步)

3. 同步性与不同步性

(1) 初始同步

  • 当页面加载时,HTML 标签的 attribute 会同步到 DOM 对象的 property。

    html
    <input id="input" type="text" value="Hello">
    javascript
    const input = document.getElementById('input');
    console.log(input.value); // 输出:Hello(初始同步)

(2) 后续不同步

  • 修改 property 不会自动更新 attribute,反之亦然。
    javascript
    input.value = 'World'; // 修改 property
    console.log(input.value); // 输出:World
    console.log(input.getAttribute('value')); // 输出:Hello(未同步)

(3) 特殊情况

  • 某些 attribute 和 property 是实时同步的,如 idclass
    javascript
    input.setAttribute('class', 'new-class');
    console.log(input.className); // 输出:new-class(同步)

4. 常见用例

(1) 获取和设置 attribute

javascript
// 获取 attribute
const type = input.getAttribute('type'); // 输出:text

// 设置 attribute
input.setAttribute('placeholder', '请输入');

(2) 获取和设置 property

javascript
// 获取 property
const value = input.value; // 输出:Hello

// 设置 property
input.value = 'World';

(3) 布尔 attribute

  • 布尔 attribute(如 disabledchecked)在 HTML 中只需存在即可生效。
  • 对应的 property 是布尔值。

示例

html
<input id="checkbox" type="checkbox" checked>
javascript
const checkbox = document.getElementById('checkbox');
console.log(checkbox.getAttribute('checked')); // 输出:空字符串
console.log(checkbox.checked); // 输出:true

5. 注意事项

  1. 数据类型差异

    • attribute 始终是字符串。
    • property 可以是任意类型。
    javascript
    input.setAttribute('maxlength', '10'); // attribute 是字符串
    console.log(typeof input.maxLength); // 输出:number(property 是数字)
  2. 性能差异

    • 操作 property 通常比操作 attribute 更快,因为 property 直接访问 DOM 对象。
  3. 自定义 attribute

    • 自定义 attribute 不会自动映射到 property。
    • 使用 data-* 属性存储自定义数据。
    html
    <div id="myDiv" data-custom="123"></div>
    javascript
    const div = document.getElementById('myDiv');
    console.log(div.dataset.custom); // 输出:123

总结

场景使用 Attribute使用 Property
获取初始值getAttribute()直接访问 property
设置值setAttribute()直接赋值 property
数据类型始终是字符串可以是任意类型
同步性初始同步,后续可能不同步动态更新,可能影响 attribute
布尔值处理存在即生效布尔值(true/false

理解 attribute 和 property 的区别有助于更好地操作 DOM 元素,避免因数据类型和同步性问题导致的错误。

document.onload和document.ready两个事件的区别

在 JavaScript 和 jQuery 中,document.onloaddocument.ready(或 $(document).ready())是两个常用的事件,但它们的行为和触发时机有显著区别。以下是它们的详细对比:

1. document.onload

(1) 定义

  • window.onload:当整个页面(包括 HTML、CSS、图片、脚本等)完全加载后触发。
  • document.onload:通常指 window.onload,因为 document 对象没有直接的 onload 事件。

(2) 触发时机

  • 页面所有资源(如图片、样式表、脚本等)加载完成后触发。

(3) 代码示例

javascript
window.onload = function() {
  console.log('页面完全加载完成');
};

(4) 特点

  • 优点:确保所有资源加载完成后再执行脚本。
  • 缺点:如果页面资源较多(如图片、视频),可能导致脚本延迟执行。

2. document.ready

(1) 定义

  • $(document).ready():jQuery 提供的方法,当 DOM 树加载完成后触发(无需等待图片等资源加载)。
  • 原生 JavaScript 替代DOMContentLoaded 事件。

(2) 触发时机

  • 当 HTML 文档完全解析并构建 DOM 树后触发,无需等待外部资源(如图片)加载。

(3) 代码示例

  • jQuery
    javascript
    $(document).ready(function() {
      console.log('DOM 加载完成');
    });
  • 原生 JavaScript
    javascript
    document.addEventListener('DOMContentLoaded', function() {
      console.log('DOM 加载完成');
    });

(4) 特点

  • 优点:尽早执行脚本,提升用户体验。
  • 缺点:无法访问未加载的资源(如图片尺寸)。

3. 主要区别

特性window.onload$(document).ready() / DOMContentLoaded
触发时机页面所有资源加载完成后触发DOM 树构建完成后触发,无需等待资源加载
执行速度较慢(需等待所有资源加载)较快(只需 DOM 解析完成)
适用场景需要操作图片、样式等资源的场景需要尽早操作 DOM 的场景
兼容性所有浏览器支持DOMContentLoaded 支持现代浏览器,jQuery 兼容性更好
多次绑定会覆盖之前的事件处理函数可多次绑定,不会覆盖

4. 示例对比

(1) window.onload

javascript
window.onload = function() {
  console.log('页面完全加载完成');
  const img = document.getElementById('myImage');
  console.log('图片宽度:', img.width); // 可以获取图片尺寸
};

(2) $(document).ready()

javascript
$(document).ready(function() {
  console.log('DOM 加载完成');
  const img = document.getElementById('myImage');
  console.log('图片宽度:', img.width); // 可能为 0(图片未加载完成)
});

(3) DOMContentLoaded

javascript
document.addEventListener('DOMContentLoaded', function() {
  console.log('DOM 加载完成');
  const img = document.getElementById('myImage');
  console.log('图片宽度:', img.width); // 可能为 0(图片未加载完成)
});

5. 使用建议

  1. 尽早操作 DOM:使用 $(document).ready()DOMContentLoaded
  2. 操作资源:使用 window.onload
  3. 多次绑定$(document).ready() 支持多次绑定,而 window.onload 会覆盖之前的事件处理函数。

总结

场景使用 window.onload使用 $(document).ready() / DOMContentLoaded
触发时机页面所有资源加载完成后DOM 树构建完成后
执行速度较慢较快
适用场景需要操作图片、样式等资源的场景需要尽早操作 DOM 的场景
兼容性所有浏览器支持DOMContentLoaded 支持现代浏览器,jQuery 兼容性更好
多次绑定会覆盖之前的事件处理函数可多次绑定,不会覆盖

根据具体需求选择合适的事件,可以提升页面性能和用户体验。

JavaScript==和===有什么区别

在 JavaScript 中,===== 是两种比较运算符,它们的主要区别在于 类型检查类型转换。以下是它们的详细对比:

1. ==(宽松相等)

(1) 定义

  • 宽松相等:比较两个值是否相等,允许类型转换。
  • 类型转换规则
    • 如果类型不同,会尝试将值转换为相同类型后再比较。
    • 转换规则复杂,可能导致意外结果。

(2) 示例

javascript
console.log(1 == '1'); // true(字符串 '1' 转换为数字 1)
console.log(true == 1); // true(布尔值 true 转换为数字 1)
console.log(null == undefined); // true(特殊规则)
console.log([] == false); // true(空数组转换为数字 0)

(3) 类型转换规则

  • 数字与字符串:字符串转换为数字。
  • 布尔值与其他类型:布尔值转换为数字(true1false0)。
  • 对象与原始值:对象通过 valueOf()toString() 转换为原始值。

2. ===(严格相等)

(1) 定义

  • 严格相等:比较两个值是否相等,不允许类型转换。
  • 类型检查:如果类型不同,直接返回 false

(2) 示例

javascript
console.log(1 === '1'); // false(类型不同)
console.log(true === 1); // false(类型不同)
console.log(null === undefined); // false(类型不同)
console.log([] === false); // false(类型不同)

(3) 特点

  • 更安全:避免类型转换导致的意外结果。
  • 更直观:类型和值都必须相同才返回 true

3. 主要区别

特性==(宽松相等)===(严格相等)
类型检查允许类型转换不允许类型转换
性能较慢(需进行类型转换)较快(直接比较)
安全性可能导致意外结果更安全,避免意外结果
推荐使用不推荐推荐

4. 特殊比较规则

(1) nullundefined

  • ==null == undefined 返回 true
  • ===null === undefined 返回 false

(2) NaN

  • NaN 与任何值比较(包括自身)都返回 false
    javascript
    console.log(NaN == NaN); // false
    console.log(NaN === NaN); // false

(3) 对象比较

  • =====:比较的是引用地址,而非内容。
    javascript
    const obj1 = {};
    const obj2 = {};
    console.log(obj1 == obj2); // false
    console.log(obj1 === obj2); // false

5. 使用建议

  1. 优先使用 ===:避免类型转换导致的意外结果,代码更安全、可读性更高。
  2. 仅在明确需要类型转换时使用 ==:例如判断变量是否为 nullundefined
    javascript
    if (value == null) {
      // 等同于 value === null || value === undefined
    }

6. 示例对比

(1) 数字与字符串

javascript
console.log(0 == '0'); // true(字符串 '0' 转换为数字 0)
console.log(0 === '0'); // false(类型不同)

(2) 布尔值与其他类型

javascript
console.log(false == '0'); // true(字符串 '0' 转换为数字 0,false 也转换为 0)
console.log(false === '0'); // false(类型不同)

(3) 对象与原始值

javascript
console.log([] == 0); // true(空数组转换为空字符串 '',再转换为数字 0)
console.log([] === 0); // false(类型不同)

总结

场景使用 ==使用 ===
类型检查允许类型转换不允许类型转换
安全性可能导致意外结果更安全,避免意外结果
推荐使用仅在明确需要类型转换时使用优先使用

理解 ===== 的区别有助于编写更健壮、可维护的 JavaScript 代码。

如何从浏览器的URL中获取查询字符串参数

从浏览器的 URL 中获取查询字符串参数是 Web 开发中的常见需求。以下是几种实现方法,涵盖原生 JavaScript 和现代 API 的使用。

1. 使用 URLSearchParams(推荐)

URLSearchParams 是一个现代 API,专门用于处理 URL 查询字符串。

(1) 获取所有参数

javascript
const urlParams = new URLSearchParams(window.location.search);

// 遍历所有参数
for (const [key, value] of urlParams.entries()) {
  console.log(`${key}: ${value}`);
}

(2) 获取单个参数

javascript
const paramValue = urlParams.get('paramName');
console.log(paramValue); // 输出参数值,若不存在则返回 null

(3) 检查参数是否存在

javascript
if (urlParams.has('paramName')) {
  console.log('参数存在');
}

(4) 示例

假设 URL 为:https://example.com/page?name=John&age=30

javascript
const urlParams = new URLSearchParams(window.location.search);
console.log(urlParams.get('name')); // 输出:John
console.log(urlParams.get('age'));  // 输出:30

2. 使用原生 JavaScript

如果不支持 URLSearchParams,可以使用原生 JavaScript 解析查询字符串。

(1) 解析查询字符串

javascript
function getQueryParams() {
  const queryString = window.location.search.substring(1); // 去掉开头的 '?'
  const params = {};

  queryString.split('&').forEach(pair => {
    const [key, value] = pair.split('=');
    params[decodeURIComponent(key)] = decodeURIComponent(value || '');
  });

  return params;
}

const params = getQueryParams();
console.log(params.name); // 输出:John
console.log(params.age);  // 输出:30

(2) 示例

假设 URL 为:https://example.com/page?name=John&age=30

javascript
const params = getQueryParams();
console.log(params.name); // 输出:John
console.log(params.age);  // 输出:30

3. 使用正则表达式

如果需要更灵活的处理,可以使用正则表达式提取参数。

(1) 提取单个参数

javascript
function getQueryParam(param) {
  const regex = new RegExp(`[?&]${param}=([^&]*)`);
  const match = window.location.search.match(regex);
  return match ? decodeURIComponent(match[1]) : null;
}

console.log(getQueryParam('name')); // 输出:John
console.log(getQueryParam('age'));  // 输出:30

(2) 示例

假设 URL 为:https://example.com/page?name=John&age=30

javascript
console.log(getQueryParam('name')); // 输出:John
console.log(getQueryParam('age'));  // 输出:30

4. 使用第三方库

如果需要处理复杂的 URL 或兼容性要求较高,可以使用第三方库,如:

  • qs:支持嵌套对象和数组的解析。
  • query-string:功能强大,支持多种格式。

(1) 使用 query-string

javascript
import queryString from 'query-string';

const params = queryString.parse(window.location.search);
console.log(params.name); // 输出:John
console.log(params.age);  // 输出:30

总结

方法优点缺点适用场景
URLSearchParams现代 API,简单易用兼容性较差(IE 不支持)现代浏览器环境
原生 JavaScript兼容性好代码较多,功能有限兼容性要求高的环境
正则表达式灵活,适合特定需求代码复杂,不易维护需要精确匹配的场景
第三方库功能强大,支持复杂格式增加项目依赖复杂 URL 处理

根据项目需求选择合适的方法,推荐优先使用 URLSearchParams

什么是三元表达式

三元表达式(Ternary Expression)是 JavaScript 中的一种简洁的条件语句,用于根据条件返回不同的值。它的语法如下:

javascript
条件 ? 表达式1 : 表达式2
  • 条件:一个布尔表达式,结果为 truefalse
  • 表达式1:如果条件为 true,返回该表达式的值。
  • 表达式2:如果条件为 false,返回该表达式的值。

1. 基本用法

三元表达式可以替代简单的 if-else 语句,使代码更简洁。

(1) 示例

javascript
const age = 20;
const message = age >= 18 ? '成年人' : '未成年人';
console.log(message); // 输出:成年人

(2) 等价于 if-else

javascript
let message;
if (age >= 18) {
  message = '成年人';
} else {
  message = '未成年人';
}

2. 嵌套三元表达式

三元表达式可以嵌套使用,但过度嵌套会降低代码可读性。

(1) 示例

javascript
const score = 85;
const grade = score >= 90 ? 'A' :
              score >= 80 ? 'B' :
              score >= 70 ? 'C' : 'D';
console.log(grade); // 输出:B

(2) 等价于 if-else if-else

javascript
let grade;
if (score >= 90) {
  grade = 'A';
} else if (score >= 80) {
  grade = 'B';
} else if (score >= 70) {
  grade = 'C';
} else {
  grade = 'D';
}

3. 使用场景

(1) 赋值

javascript
const isLoggedIn = true;
const welcomeMessage = isLoggedIn ? '欢迎回来!' : '请登录';
console.log(welcomeMessage); // 输出:欢迎回来!

(2) 返回值

javascript
function getFee(isMember) {
  return isMember ? '$2.00' : '$10.00';
}
console.log(getFee(true)); // 输出:$2.00

(3) 条件渲染(如 React)

javascript
const isAdmin = true;
const button = isAdmin ? <AdminButton /> : <UserButton />;

4. 注意事项

  1. 可读性:避免过度嵌套三元表达式,否则会降低代码可读性。

    javascript
    // 不推荐
    const result = condition1 ? value1 :
                   condition2 ? value2 :
                   condition3 ? value3 : value4;
  2. 性能:三元表达式与 if-else 性能相近,选择时以可读性为主。

  3. 副作用:避免在三元表达式中执行有副作用的操作(如赋值、函数调用)。

    javascript
    // 不推荐
    const value = condition ? (a = 10) : (b = 20);

总结

特性三元表达式if-else
语法条件 ? 表达式1 : 表达式2if (条件) { ... } else { ... }
简洁性更简洁较冗长
可读性适合简单条件适合复杂逻辑
嵌套可嵌套,但不宜过多可嵌套,适合复杂条件

三元表达式是 JavaScript 中一种简洁的条件语句,适合处理简单的条件逻辑,但需注意代码可读性。

JavaScript中use strict是什么,使用他的优缺点

use strict 是 JavaScript 中的一种 严格模式,用于在更严格的条件下执行代码。它可以帮助开发者避免常见的错误,并提高代码的安全性和性能。

1. 什么是 use strict

  • 定义use strict 是一种指令,用于启用 JavaScript 的严格模式。
  • 作用:在严格模式下,JavaScript 引擎会对代码进行更严格的解析和错误检查。
  • 启用方式
    • 全局启用:在脚本文件或 <script> 标签的开头添加 "use strict";
    • 局部启用:在函数内部添加 "use strict";

2. 严格模式的主要特性

(1) 禁止意外创建全局变量

  • 非严格模式下,未声明的变量会被隐式创建为全局变量。
  • 严格模式下会抛出错误。

示例

javascript
"use strict";
x = 10; // 报错:ReferenceError: x is not defined

(2) 禁止删除不可删除的属性

  • 非严格模式下,删除不可删除的属性(如 delete Object.prototype)会静默失败。
  • 严格模式下会抛出错误。

示例

javascript
"use strict";
delete Object.prototype; // 报错:TypeError: Cannot delete property 'prototype' of Object

(3) 禁止重复的参数名

  • 非严格模式下,函数允许重复的参数名。
  • 严格模式下会抛出错误。

示例

javascript
"use strict";
function sum(a, a, c) { // 报错:SyntaxError: Duplicate parameter name not allowed in this context
  return a + a + c;
}

(4) 禁止使用 with 语句

  • with 语句会导致代码难以理解和优化。
  • 严格模式下会抛出错误。

示例

javascript
"use strict";
with (Math) { // 报错:SyntaxError: Strict mode code may not include a with statement
  console.log(PI);
}

(5) 禁止 this 指向全局对象

  • 非严格模式下,函数中的 this 默认指向全局对象(如 window)。
  • 严格模式下,thisundefined

示例

javascript
"use strict";
function test() {
  console.log(this); // 输出:undefined
}
test();

(6) 禁止八进制字面量

  • 非严格模式下,以 0 开头的数字会被解析为八进制。
  • 严格模式下会抛出错误。

示例

javascript
"use strict";
const num = 0123; // 报错:SyntaxError: Octal literals are not allowed in strict mode

3. 使用 use strict 的优点

  1. 减少错误:避免隐式创建全局变量、意外删除属性等常见错误。
  2. 提高安全性:防止不安全的操作(如 eval 修改作用域)。
  3. 优化性能:帮助 JavaScript 引擎更好地优化代码。
  4. 更好的代码质量:强制使用更严格的语法,提升代码可维护性。

4. 使用 use strict 的缺点

  1. 兼容性问题:旧版浏览器可能不支持严格模式。
  2. 代码迁移成本:将非严格模式代码迁移到严格模式可能需要大量修改。
  3. 学习成本:开发者需要了解严格模式的规则和限制。

5. 如何启用严格模式

(1) 全局启用

在脚本文件或 <script> 标签的开头添加 "use strict";

javascript
"use strict";
// 严格模式下的代码

(2) 局部启用

在函数内部添加 "use strict";

javascript
function strictFunction() {
  "use strict";
  // 严格模式下的代码
}

总结

特性严格模式非严格模式
全局变量禁止隐式创建允许隐式创建
删除属性禁止删除不可删除的属性静默失败
参数名禁止重复允许重复
with 语句禁止使用允许使用
this 指向函数中为undefined指向全局对象
八进制禁止使用允许使用

建议:在现代 JavaScript 开发中,推荐始终使用严格模式,以提高代码质量和安全性。

JavaScript中isNaN函数

isNaN 是 JavaScript 中的一个全局函数,用于检查一个值是否为 NaN(Not-a-Number)。然而,它的行为有时会让人感到困惑,因此需要结合 Number.isNaN 来理解其用法。

1. isNaN 的功能

  • 作用:检查传入的值是否是 NaN
  • 返回值:如果值是 NaN,返回 true;否则返回 false

2. isNaN 的问题

  • 行为怪异isNaN 会先将参数转换为数字,然后再检查是否为 NaN
  • 结果可能不符合预期:对于非数字的值(如字符串),isNaN 也会返回 true

示例

javascript
console.log(isNaN(123)); // false(数字,不是 NaN)
console.log(isNaN('123')); // false(字符串 '123' 转换为数字 123,不是 NaN)
console.log(isNaN('Hello')); // true(字符串 'Hello' 转换为数字失败,返回 NaN)
console.log(isNaN(NaN)); // true(NaN)

3. Number.isNaN 的改进

  • 作用:严格检查一个值是否为 NaN,不会进行类型转换。
  • 返回值:只有当值是 NaN 时,返回 true;否则返回 false

示例

javascript
console.log(Number.isNaN(123)); // false(数字,不是 NaN)
console.log(Number.isNaN('123')); // false(字符串,不是 NaN)
console.log(Number.isNaN('Hello')); // false(字符串,不是 NaN)
console.log(Number.isNaN(NaN)); // true(NaN)

4. 使用场景

(1) 检查 NaN

  • 使用 Number.isNaN 严格检查 NaN
    javascript
    const value = parseInt('abc');
    if (Number.isNaN(value)) {
      console.log('解析失败,值为 NaN');
    }

(2) 检查非数字值

  • 如果需要检查一个值是否为数字,可以使用 isNaN 结合类型检查。
    javascript
    function isNotNumber(value) {
      return typeof value !== 'number' || isNaN(value);
    }
    console.log(isNotNumber('123')); // true(字符串,不是数字)
    console.log(isNotNumber(NaN)); // true(NaN)
    console.log(isNotNumber(123)); // false(数字)

总结

函数行为示例推荐使用
isNaN先转换类型,再检查是否为NaNisNaN('Hello')true不推荐
Number.isNaN严格检查是否为NaN,不转换类型Number.isNaN('Hello')false推荐
  • 推荐使用 Number.isNaN:避免类型转换带来的意外行为。
  • 避免直接使用 isNaN:除非明确需要类型转换的逻辑。

JavaScript中负无穷大是什么

在 JavaScript 中,负无穷大(Negative Infinity) 是一个特殊的数值,表示比任何有限数都小的值。它是全局对象 Number 的一个属性,通常用于表示数学运算中的下溢或无限小的概念。

1. 负无穷大的表示

  • 字面量-Infinity
  • 属性Number.NEGATIVE_INFINITY

示例

javascript
console.log(-Infinity); // 输出:-Infinity
console.log(Number.NEGATIVE_INFINITY); // 输出:-Infinity

2. 负无穷大的产生

负无穷大通常由以下操作产生:

(1) 数学运算

  • 除以 0 的负数。
  • 超出数值范围的下限。

示例

javascript
console.log(-1 / 0); // 输出:-Infinity
console.log(Number.MIN_VALUE / 0); // 输出:-Infinity

(2) 显式赋值

javascript
const negativeInfinity = -Infinity;
console.log(negativeInfinity); // 输出:-Infinity

3. 负无穷大的特性

(1) 比较

  • 负无穷大比任何有限数都小。
  • 负无穷大等于自身。

示例

javascript
console.log(-Infinity < -1000000); // 输出:true
console.log(-Infinity === -Infinity); // 输出:true

(2) 数学运算

  • 负无穷大参与的运算结果通常为负无穷大或 NaN

示例

javascript
console.log(-Infinity + 10); // 输出:-Infinity
console.log(-Infinity * 2); // 输出:-Infinity
console.log(-Infinity / -Infinity); // 输出:NaN

(3) 类型

  • 负无穷大的类型是 number

示例

javascript
console.log(typeof -Infinity); // 输出:number

4. 负无穷大的应用场景

(1) 初始化最小值

  • 在查找最小值时,可以用负无穷大作为初始值。

示例

javascript
let max = -Infinity;
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(num => {
  if (num > max) {
    max = num;
  }
});
console.log(max); // 输出:5

(2) 数学计算

  • 用于表示无限小的概念。

示例

javascript
function calculateLimit() {
  return -Infinity;
}
console.log(calculateLimit()); // 输出:-Infinity

总结

特性描述示例
表示-InfinityNumber.NEGATIVE_INFINITYconsole.log(-Infinity)
比较比任何有限数都小-Infinity < -1000000true
运算通常结果为-InfinityNaN-Infinity + 10-Infinity
类型numbertypeof -Infinitynumber

负无穷大是 JavaScript 中表示无限小的特殊值,常用于数学计算和初始化场景。理解其特性有助于更好地处理数值运算。

JavaScript中NaN

在 JavaScript 中,NaN 是一个特殊的数值,表示 “Not a Number”(非数字)。它通常用于表示数学运算中无法表示的结果,例如无效的算术运算或无法解析的字符串。

1. NaN 的特性

(1) 类型

  • NaN 的类型是 number
    javascript
    console.log(typeof NaN); // 输出:number

(2) 不等于自身

  • NaN 是 JavaScript 中唯一一个不等于自身的值。
    javascript
    console.log(NaN === NaN); // 输出:false

(3) 全局属性

  • NaN 是全局对象的一个属性,可以通过 NaNNumber.NaN 访问。
    javascript
    console.log(NaN); // 输出:NaN
    console.log(Number.NaN); // 输出:NaN

2. NaN 的产生

NaN 通常由以下操作产生:

(1) 无效的数学运算

  • 0 除以 0。
  • 无穷大除以无穷大。
  • 负数开平方。

示例

javascript
console.log(0 / 0); // 输出:NaN
console.log(Infinity / Infinity); // 输出:NaN
console.log(Math.sqrt(-1)); // 输出:NaN

(2) 无法解析的字符串

  • 使用 parseIntparseFloat 解析非数字字符串。

示例

javascript
console.log(parseInt('Hello')); // 输出:NaN
console.log(parseFloat('abc')); // 输出:NaN

(3) 显式赋值

javascript
const result = NaN;
console.log(result); // 输出:NaN

3. 检测 NaN

由于 NaN 不等于自身,因此不能直接使用 ===== 检测。以下是几种检测 NaN 的方法:

(1) 使用 isNaN 函数

  • 问题isNaN 会先将参数转换为数字,再检查是否为 NaN
  • 行为:对于非数字的值(如字符串),isNaN 也会返回 true

示例

javascript
console.log(isNaN(NaN)); // 输出:true
console.log(isNaN('Hello')); // 输出:true(字符串 'Hello' 转换为数字失败,返回 NaN)

(2) 使用 Number.isNaN 函数

  • 改进Number.isNaN 严格检查一个值是否为 NaN,不会进行类型转换。

示例

javascript
console.log(Number.isNaN(NaN)); // 输出:true
console.log(Number.isNaN('Hello')); // 输出:false(字符串,不是 NaN)

(3) 使用 Object.is 方法

  • 作用:严格比较两个值是否相同,包括 NaN

示例

javascript
console.log(Object.is(NaN, NaN)); // 输出:true

4. 处理 NaN

(1) 检查并处理

  • 在数学运算后检查结果是否为 NaN,并进行相应处理。

示例

javascript
const result = Math.sqrt(-1);
if (Number.isNaN(result)) {
  console.log('计算结果无效');
}

(2) 默认值

  • 使用 ||?? 运算符为 NaN 提供默认值。

示例

javascript
const value = parseInt('abc') || 0; // 如果解析失败,返回 0
console.log(value); // 输出:0

总结

特性描述示例
类型numbertypeof NaNnumber
比较不等于自身NaN === NaNfalse
检测使用Number.isNaNObject.isNumber.isNaN(NaN)true
产生原因无效的数学运算或无法解析的字符串0 / 0NaN

NaN 是 JavaScript 中表示无效数值的特殊值,理解其特性和检测方法有助于更好地处理数学运算中的异常情况。

JavaScript展开运算符

展开运算符(Spread Operator) 是 JavaScript 中的一种语法,使用 ... 表示。它可以将数组、对象或字符串“展开”为独立的元素,常用于函数调用、数组操作和对象操作。

1. 展开运算符的基本用法

(1) 展开数组

  • 将数组展开为独立的元素。

示例

javascript
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // 输出:[1, 2, 3, 4, 5]

(2) 展开对象

  • 将对象展开为独立的键值对。

示例

javascript
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };
console.log(obj2); // 输出:{ a: 1, b: 2, c: 3 }

(3) 展开字符串

  • 将字符串展开为独立的字符。

示例

javascript
const str = 'hello';
const chars = [...str];
console.log(chars); // 输出:['h', 'e', 'l', 'l', 'o']

2. 展开运算符的应用场景

(1) 函数调用

  • 将数组展开为函数的参数。

示例

javascript
function sum(a, b, c) {
  return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 输出:6

(2) 数组操作

  • 合并数组。
  • 复制数组。

示例

javascript
// 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2];
console.log(merged); // 输出:[1, 2, 3, 4]

// 复制数组
const original = [1, 2, 3];
const copy = [...original];
console.log(copy); // 输出:[1, 2, 3]

(3) 对象操作

  • 合并对象。
  • 复制对象。

示例

javascript
// 合并对象
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 };
console.log(merged); // 输出:{ a: 1, b: 2, c: 3, d: 4 }

// 复制对象
const original = { a: 1, b: 2 };
const copy = { ...original };
console.log(copy); // 输出:{ a: 1, b: 2 }

(4) 字符串操作

  • 将字符串转换为数组。

示例

javascript
const str = 'hello';
const chars = [...str];
console.log(chars); // 输出:['h', 'e', 'l', 'l', 'o']

3. 展开运算符的注意事项

(1) 浅拷贝

  • 展开运算符只能进行浅拷贝,嵌套对象或数组不会被深拷贝。
javascript
const original = { a: { b: 1 } };
const copy = { ...original };
copy.a.b = 2;
console.log(original.a.b); // 输出:2(原对象也被修改)

(2) 对象展开的顺序

  • 如果对象中有重复的键,后面的值会覆盖前面的值。
javascript
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 };
console.log(merged); // 输出:{ a: 1, b: 3, c: 4 }

(3) 不可展开非可迭代对象

  • 展开运算符只能用于可迭代对象(如数组、字符串、Map、Set 等)。
javascript
const obj = { a: 1, b: 2 };
const arr = [...obj]; // 报错:TypeError: obj is not iterable

总结

场景示例描述
函数调用sum(...numbers)将数组展开为函数参数
数组合并[...arr1, ...arr2]合并多个数组
数组复制[...original]创建数组的浅拷贝
对象合并{ ...obj1, ...obj2 }合并多个对象
对象复制{ ...original }创建对象的浅拷贝
字符串展开[...str]将字符串展开为字符数组

展开运算符是 JavaScript 中非常实用的语法,可以简化数组、对象和字符串的操作,提升代码的可读性和简洁性。

JavaScript如何处理异常

在 JavaScript 中,异常处理 是通过 try...catch...finally 语句实现的。它允许开发者捕获并处理运行时错误,避免程序崩溃。以下是异常处理的详细说明:

1. try...catch...finally 语句

(1) 语法

javascript
try {
  // 可能抛出错误的代码
} catch (error) {
  // 捕获并处理错误
} finally {
  // 无论是否抛出错误,都会执行的代码
}

(2) 各部分作用

  • try:包裹可能抛出错误的代码。
  • catch:捕获错误并处理。
  • finally:无论是否抛出错误,都会执行的代码(常用于清理资源)。

(3) 示例

javascript
try {
  const result = 10 / 0;
  if (!isFinite(result)) {
    throw new Error('计算结果无效');
  }
} catch (error) {
  console.log('捕获错误:', error.message); // 输出:捕获错误:计算结果无效
} finally {
  console.log('执行完毕');
}

2. 抛出异常

(1) throw 语句

  • 用于手动抛出异常。
  • 可以抛出任何类型的值(如字符串、数字、对象等),但通常抛出 Error 对象。

示例

javascript
function divide(a, b) {
  if (b === 0) {
    throw new Error('除数不能为 0');
  }
  return a / b;
}

try {
  console.log(divide(10, 0));
} catch (error) {
  console.log('捕获错误:', error.message); // 输出:捕获错误:除数不能为 0
}

(2) 自定义错误类型

  • 可以通过继承 Error 创建自定义错误类型。

示例

javascript
class CustomError extends Error {
  constructor(message) {
    super(message);
    this.name = 'CustomError';
  }
}

try {
  throw new CustomError('自定义错误');
} catch (error) {
  console.log('捕获错误:', error.name, error.message); // 输出:捕获错误:CustomError 自定义错误
}

3. 异常处理的注意事项

(1) 捕获特定类型的错误

  • 使用 instanceof 检查错误类型。

示例

javascript
try {
  throw new TypeError('类型错误');
} catch (error) {
  if (error instanceof TypeError) {
    console.log('捕获类型错误:', error.message);
  } else {
    console.log('捕获其他错误:', error.message);
  }
}

(2) 避免空的 catch

  • 空的 catch 块会隐藏错误,导致难以调试。

错误示例

javascript
try {
  throw new Error('错误');
} catch (error) {
  // 空的 catch 块
}

正确示例

javascript
try {
  throw new Error('错误');
} catch (error) {
  console.log('捕获错误:', error.message);
}

(3) finally 块的作用

  • finally 块中的代码无论是否抛出错误都会执行。
  • 常用于释放资源(如关闭文件、清理定时器等)。

示例

javascript
let resource = null;
try {
  resource = allocateResource();
  useResource(resource);
} catch (error) {
  console.log('捕获错误:', error.message);
} finally {
  if (resource) {
    releaseResource(resource);
  }
}

4. 异步代码中的异常处理

(1) Promise 的异常处理

  • 使用 .catch() 捕获 Promise 中的错误。

示例

javascript
fetch('https://example.com')
  .then(response => response.json())
  .catch(error => {
    console.log('捕获错误:', error.message);
  });

(2) async/await 的异常处理

  • 使用 try...catch 捕获 async 函数中的错误。

示例

javascript
async function fetchData() {
  try {
    const response = await fetch('https://example.com');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log('捕获错误:', error.message);
  }
}
fetchData();

总结

特性描述示例
try包裹可能抛出错误的代码try { ... }
catch捕获并处理错误catch (error) { ... }
finally无论是否抛出错误,都会执行的代码finally { ... }
throw手动抛出异常throw new Error('错误')
异步处理使用.catch()try...catchasync function() { try { ... } catch { ... } }

异常处理是 JavaScript 中保证程序健壮性的重要机制,合理使用 try...catch...finally 可以有效捕获和处理错误,避免程序崩溃。

JavaScript如何删除属性及其值

在JavaScript中,可以使用delete操作符来删除对象的属性及其值。delete操作符会从对象中移除指定的属性,如果删除成功,返回true;如果属性不存在或无法删除,返回false

示例代码

javascript
let obj = {
    name: "Alice",
    age: 25,
    city: "New York"
};

// 删除 'age' 属性
delete obj.age;

console.log(obj); // 输出: { name: "Alice", city: "New York" }

// 尝试删除不存在的属性
let result = delete obj.gender;
console.log(result); // 输出: true (即使属性不存在,delete 也会返回 true)

// 删除成功
result = delete obj.city;
console.log(result); // 输出: true
console.log(obj); // 输出: { name: "Alice" }

注意事项

  1. 不可配置的属性:如果属性是不可配置的(即configurablefalse),delete操作符无法删除该属性,并且会返回false

    javascript
    let obj = {};
    Object.defineProperty(obj, 'name', {
        value: 'Alice',
        configurable: false
    });
    
    let result = delete obj.name;
    console.log(result); // 输出: false
    console.log(obj.name); // 输出: "Alice"
  2. 全局变量和函数delete操作符不能删除使用varletconst声明的变量或函数。

    javascript
    var x = 10;
    console.log(delete x); // 输出: false
    
    function foo() {}
    console.log(delete foo); // 输出: false
  3. 数组元素:虽然可以使用delete删除数组元素,但这会在数组中留下一个undefined的空位,而不会改变数组的长度。

    javascript
    let arr = [1, 2, 3];
    delete arr[1];
    console.log(arr); // 输出: [1, undefined, 3]
    console.log(arr.length); // 输出: 3

总结

  • delete操作符用于删除对象的属性。
  • 如果属性不存在或无法删除,delete会返回true
  • delete不能删除使用varletconst声明的变量或函数。
  • 删除数组元素会留下undefined的空位,不会改变数组长度。