JS相关知识4.0
JavaScript中setTimeout()方法
在 JavaScript 中,setTimeout()
是一个用于在指定时间后执行代码的全局函数。它允许你延迟执行一段代码或函数,通常用于实现定时任务、延迟操作或异步行为。
基本语法
setTimeout(callback, delay, arg1, arg2, ...);
callback
:要执行的函数或代码块。delay
:延迟的时间(以毫秒为单位)。默认值为0
,表示尽快执行。arg1, arg2, ...
(可选):传递给回调函数的参数。
示例代码
基本用法:
javascriptsetTimeout(function() { console.log('Hello after 2 seconds'); }, 2000); // 2 秒后执行
传递参数:
javascriptsetTimeout(function(name, age) { console.log(`Hello, ${name}! You are ${age} years old.`); }, 1000, 'Alice', 25); // 1 秒后执行,并传递参数
使用箭头函数:
javascriptsetTimeout(() => { console.log('This is an arrow function'); }, 500); // 0.5 秒后执行
立即执行(
delay
为 0):javascriptsetTimeout(function() { console.log('This runs as soon as possible'); }, 0); // 尽快执行
返回值
setTimeout()
返回一个唯一的 定时器 ID(数字类型),可以用于后续取消定时器。- 使用
clearTimeout()
方法可以取消尚未执行的定时器。
let timerId = setTimeout(function() {
console.log('This will not run');
}, 1000);
clearTimeout(timerId); // 取消定时器
注意事项
异步执行:
setTimeout()
是异步的,即使延迟时间为0
,回调函数也会被放入事件队列,等待当前代码执行完毕后再执行。
javascriptconsole.log('Start'); setTimeout(() => { console.log('Inside setTimeout'); }, 0); console.log('End'); // 输出顺序: // Start // End // Inside setTimeout
this
绑定:- 在非箭头函数中,
setTimeout
回调函数中的this
默认指向全局对象(浏览器中为window
,Node.js 中为global
)。如果需要绑定特定的this
,可以使用bind
或箭头函数。
javascriptlet 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
- 在非箭头函数中,
最小延迟时间:
- 浏览器中,
setTimeout
的最小延迟时间通常为4ms
(即使设置为0
)。这是为了防止过度消耗资源。
- 浏览器中,
嵌套
setTimeout
:- 可以使用嵌套的
setTimeout
来实现周期性任务(类似于setInterval
),但更灵活。
javascriptlet 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 中的一个全局方法,用于按照指定的时间间隔重复执行某个函数或代码片段。它通常用于需要周期性执行任务的场景,例如定时更新页面内容、轮播图、定时请求数据等。
语法
setInterval(func, delay, arg1, arg2, ...);
func
: 要重复执行的函数或代码片段。delay
: 每次执行之间的时间间隔,以毫秒为单位(1000 毫秒 = 1 秒)。arg1, arg2, ...
(可选): 传递给func
的额外参数。
返回值
setInterval()
返回一个唯一的间隔 ID(intervalID
),这个 ID 可以用于后续清除间隔(停止重复执行)。
示例
// 每隔 1 秒打印一次 "Hello, World!"
const intervalID = setInterval(() => {
console.log("Hello, World!");
}, 1000);
// 5 秒后停止打印
setTimeout(() => {
clearInterval(intervalID);
console.log("Interval stopped.");
}, 5000);
清除间隔
使用 clearInterval()
方法可以停止 setInterval()
的执行。你需要传递 setInterval()
返回的 intervalID
作为参数。
clearInterval(intervalID);
注意事项
- 时间间隔的准确性:
setInterval()
并不能保证严格的时间间隔,因为 JavaScript 是单线程的,可能会受到其他任务的影响。 - 内存泄漏: 如果不及时清除不再需要的
setInterval()
,可能会导致内存泄漏。 - 嵌套
setInterval()
: 如果在一个setInterval()
回调函数中再次调用setInterval()
,可能会导致多个间隔同时运行,需谨慎使用。
替代方案
在某些场景下,setTimeout()
可以替代 setInterval()
,尤其是在需要动态调整间隔时间或确保前一个任务完成后再执行下一个任务时。
function repeatTask() {
console.log("Task executed");
setTimeout(repeatTask, 1000); // 1 秒后再次执行
}
repeatTask(); // 启动任务
通过这种方式,可以更好地控制任务的执行时机。
JavaScript清除定时器
在 JavaScript 中,定时器(Timer)用于延迟执行代码或周期性执行代码。常见的定时器包括 setTimeout
和 setInterval
。为了确保定时器不会在不需要时继续运行,需要手动清除定时器。以下是清除定时器的方法及示例:
1. 清除 setTimeout
定时器
setTimeout
用于延迟执行代码一次,返回一个定时器 ID。可以使用 clearTimeout
清除定时器。
示例:
// 设置定时器
const timerId = setTimeout(() => {
console.log("定时器执行");
}, 1000);
// 清除定时器
clearTimeout(timerId);
2. 清除 setInterval
定时器
setInterval
用于周期性执行代码,返回一个定时器 ID。可以使用 clearInterval
清除定时器。
示例:
// 设置定时器
const intervalId = setInterval(() => {
console.log("定时器执行");
}, 1000);
// 清除定时器
clearInterval(intervalId);
3. 清除所有定时器
如果需要清除所有定时器,可以将定时器 ID 存储在数组中,然后遍历清除。
示例:
const timerIds = [];
// 设置多个定时器
timerIds.push(setTimeout(() => console.log("定时器 1"), 1000));
timerIds.push(setTimeout(() => console.log("定时器 2"), 2000));
// 清除所有定时器
timerIds.forEach(id => clearTimeout(id));
4. 在组件或页面卸载时清除定时器
在单页面应用(SPA)或组件化开发中,确保在组件卸载时清除定时器,避免内存泄漏。
示例(React):
import React, { useEffect } from "react";
function MyComponent() {
useEffect(() => {
const timerId = setTimeout(() => {
console.log("定时器执行");
}, 1000);
// 清除定时器
return () => clearTimeout(timerId);
}, []);
return <div>组件内容</div>;
}
5. 使用 Promise
封装定时器
通过 Promise
封装定时器,可以更方便地控制定时器的执行和清除。
示例:
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
实现定时器的控制。
示例:
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 是唯一的:每次调用
setTimeout
或setInterval
都会返回一个新的 ID。 - 清除无效定时器:如果定时器已经执行或清除,再次调用
clearTimeout
或clearInterval
不会报错。 - 内存泄漏:未清除的定时器可能导致内存泄漏,尤其是在单页面应用中。
总结
方法 | 适用场景 | 示例 |
---|---|---|
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 定义了脚本语言的核心特性,包括语法、类型、语句、关键字、操作符、对象等。
关键点
历史背景:
- JavaScript 最初由 Netscape 开发,后来微软也推出了 JScript。
- 为了避免浏览器脚本语言的碎片化,Netscape 将 JavaScript 提交给 ECMA International 进行标准化,最终形成了 ECMAScript 标准。
版本演进:
- ES1 (1997):第一个正式版本,奠定了 JavaScript 的基础。
- ES3 (1999):引入了正则表达式、异常处理(
try/catch
)等特性。 - ES5 (2009):增加了严格模式(
"use strict"
)、JSON 支持、数组方法(如map
、filter
)等。 - ES6 (2015, 也称为 ES2015):重大更新,引入了
let
/const
、箭头函数、类、模块化、Promise、解构赋值等现代 JavaScript 的核心特性。 - ES2016 及以后:每年发布一个新版本,逐步增加新特性,如
async/await
、可选链操作符(?.
)、空值合并操作符(??
)等。
核心特性:
- 动态类型:变量不需要声明类型,类型在运行时确定。
- 基于原型:JavaScript 使用原型链实现继承,而不是传统的类继承(ES6 引入了
class
语法糖,但底层仍然是基于原型)。 - 函数是一等公民:函数可以作为参数传递、返回值或赋值给变量。
- 异步编程:通过回调函数、Promise 和
async/await
支持异步操作。
与 JavaScript 的关系:
- ECMAScript 是标准,JavaScript 是 ECMAScript 的实现。
- 浏览器和 Node.js 等运行环境会根据 ECMAScript 标准实现 JavaScript 引擎(如 V8、SpiderMonkey 等)。
现代 JavaScript:
- 现代 JavaScript 开发通常基于 ES6 及更高版本,结合模块化、工具链(如 Babel、Webpack)和框架(如 React、Vue)构建复杂应用。
- 新特性如箭头函数、模板字符串、解构赋值等极大地提升了开发效率和代码可读性。
示例代码
以下是 ES6 及更高版本的一些特性示例:
// 箭头函数
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. 块级作用域与 let
、const
(1) let
和 const
let
:声明块级作用域的变量。const
:声明块级作用域的常量。
示例:
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
(词法作用域)。
示例:
const add = (a, b) => a + b;
console.log(add(2, 3)); // 输出:5
3. 模板字符串(Template Literals)
- 使用反引号(
`
)定义字符串,支持多行文本和嵌入表达式。
示例:
const name = 'John';
const message = `Hello, ${name}!
Welcome to ES6.`;
console.log(message);
4. 解构赋值(Destructuring Assignment)
- 从数组或对象中提取值并赋值给变量。
示例:
// 数组解构
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)
- 为函数参数设置默认值。
示例:
function greet(name = 'Guest') {
console.log(`Hello, ${name}`);
}
greet(); // 输出:Hello, Guest
6. 剩余参数与扩展运算符
(1) 剩余参数(Rest Parameters)
- 将多个参数收集为一个数组。
示例:
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 输出:6
(2) 扩展运算符(Spread Operator)
- 将数组或对象展开。
示例:
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // 输出:[1, 2, 3, 4, 5]
7. 类与继承(Classes)
- 提供更清晰的面向对象编程语法。
示例:
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)
- 使用
import
和export
实现模块化。
示例:
// 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 与异步编程
- 提供更优雅的异步编程方式。
示例:
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('Data fetched'), 1000);
});
};
fetchData().then(data => console.log(data)); // 输出:Data fetched
10. 其他特性
(1) 对象字面量增强
- 简写属性和方法。
示例:
const name = 'John';
const obj = { name, greet() { console.log('Hello'); } };
(2) Symbol
类型
- 创建唯一标识符。
示例:
const id = Symbol('id');
(3) Map
和 Set
- 提供更强大的数据结构。
示例:
const map = new Map();
map.set('key', 'value');
console.log(map.get('key')); // 输出:value
总结
ES6 引入了许多新特性,使 JavaScript 更现代化、功能更强大。以下是主要特性的对比:
特性 | 描述 | 示例 |
---|---|---|
let /const | 块级作用域变量声明 | let x = 10; |
箭头函数 | 简洁的函数语法,自动绑定this | const add = (a, b) => a + b; |
模板字符串 | 支持多行文本和嵌入表达式 | `Hello, ${name}` |
解构赋值 | 从数组或对象中提取值 | const [a, b] = [1, 2]; |
默认参数 | 为函数参数设置默认值 | function greet(name = 'Guest') |
剩余参数与扩展运算符 | 收集参数或展开数组/对象 | const arr2 = [...arr1]; |
类与继承 | 提供面向对象编程语法 | class Dog extends Animal |
模块化 | 使用import 和 export | import { 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 开发中,attribute 和 property 是两个容易混淆的概念,它们都与 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:直接在标签中定义。
- JavaScript:
- 数据类型:始终是字符串。
- 同步性:初始时与 property 同步,后续可能不同步。
示例:
<input id="input" type="text" value="Hello">
const input = document.getElementById('input');
console.log(input.getAttribute('value')); // 输出:Hello
(2) Property
- 定义:DOM 对象上的特性,通过 JavaScript 访问。
- 访问方式:直接通过 DOM 对象访问。
- 数据类型:可以是任意类型(如布尔值、数字、对象等)。
- 同步性:动态更新,可能影响 attribute。
示例:
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">
javascriptconst 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 是实时同步的,如
id
、class
。javascriptinput.setAttribute('class', 'new-class'); console.log(input.className); // 输出:new-class(同步)
4. 常见用例
(1) 获取和设置 attribute
// 获取 attribute
const type = input.getAttribute('type'); // 输出:text
// 设置 attribute
input.setAttribute('placeholder', '请输入');
(2) 获取和设置 property
// 获取 property
const value = input.value; // 输出:Hello
// 设置 property
input.value = 'World';
(3) 布尔 attribute
- 布尔 attribute(如
disabled
、checked
)在 HTML 中只需存在即可生效。 - 对应的 property 是布尔值。
示例:
<input id="checkbox" type="checkbox" checked>
const checkbox = document.getElementById('checkbox');
console.log(checkbox.getAttribute('checked')); // 输出:空字符串
console.log(checkbox.checked); // 输出:true
5. 注意事项
数据类型差异:
- attribute 始终是字符串。
- property 可以是任意类型。
javascriptinput.setAttribute('maxlength', '10'); // attribute 是字符串 console.log(typeof input.maxLength); // 输出:number(property 是数字)
性能差异:
- 操作 property 通常比操作 attribute 更快,因为 property 直接访问 DOM 对象。
自定义 attribute:
- 自定义 attribute 不会自动映射到 property。
- 使用
data-*
属性存储自定义数据。
html<div id="myDiv" data-custom="123"></div>
javascriptconst 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.onload
和 document.ready
(或 $(document).ready()
)是两个常用的事件,但它们的行为和触发时机有显著区别。以下是它们的详细对比:
1. document.onload
(1) 定义
window.onload
:当整个页面(包括 HTML、CSS、图片、脚本等)完全加载后触发。document.onload
:通常指window.onload
,因为document
对象没有直接的onload
事件。
(2) 触发时机
- 页面所有资源(如图片、样式表、脚本等)加载完成后触发。
(3) 代码示例
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
window.onload = function() {
console.log('页面完全加载完成');
const img = document.getElementById('myImage');
console.log('图片宽度:', img.width); // 可以获取图片尺寸
};
(2) $(document).ready()
$(document).ready(function() {
console.log('DOM 加载完成');
const img = document.getElementById('myImage');
console.log('图片宽度:', img.width); // 可能为 0(图片未加载完成)
});
(3) DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM 加载完成');
const img = document.getElementById('myImage');
console.log('图片宽度:', img.width); // 可能为 0(图片未加载完成)
});
5. 使用建议
- 尽早操作 DOM:使用
$(document).ready()
或DOMContentLoaded
。 - 操作资源:使用
window.onload
。 - 多次绑定:
$(document).ready()
支持多次绑定,而window.onload
会覆盖之前的事件处理函数。
总结
场景 | 使用 window.onload | 使用 $(document).ready() / DOMContentLoaded |
---|---|---|
触发时机 | 页面所有资源加载完成后 | DOM 树构建完成后 |
执行速度 | 较慢 | 较快 |
适用场景 | 需要操作图片、样式等资源的场景 | 需要尽早操作 DOM 的场景 |
兼容性 | 所有浏览器支持 | DOMContentLoaded 支持现代浏览器,jQuery 兼容性更好 |
多次绑定 | 会覆盖之前的事件处理函数 | 可多次绑定,不会覆盖 |
根据具体需求选择合适的事件,可以提升页面性能和用户体验。
JavaScript==和===有什么区别
在 JavaScript 中,==
和 ===
是两种比较运算符,它们的主要区别在于 类型检查 和 类型转换。以下是它们的详细对比:
1. ==
(宽松相等)
(1) 定义
- 宽松相等:比较两个值是否相等,允许类型转换。
- 类型转换规则:
- 如果类型不同,会尝试将值转换为相同类型后再比较。
- 转换规则复杂,可能导致意外结果。
(2) 示例
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) 类型转换规则
- 数字与字符串:字符串转换为数字。
- 布尔值与其他类型:布尔值转换为数字(
true
→1
,false
→0
)。 - 对象与原始值:对象通过
valueOf()
或toString()
转换为原始值。
2. ===
(严格相等)
(1) 定义
- 严格相等:比较两个值是否相等,不允许类型转换。
- 类型检查:如果类型不同,直接返回
false
。
(2) 示例
console.log(1 === '1'); // false(类型不同)
console.log(true === 1); // false(类型不同)
console.log(null === undefined); // false(类型不同)
console.log([] === false); // false(类型不同)
(3) 特点
- 更安全:避免类型转换导致的意外结果。
- 更直观:类型和值都必须相同才返回
true
。
3. 主要区别
特性 | == (宽松相等) | === (严格相等) |
---|---|---|
类型检查 | 允许类型转换 | 不允许类型转换 |
性能 | 较慢(需进行类型转换) | 较快(直接比较) |
安全性 | 可能导致意外结果 | 更安全,避免意外结果 |
推荐使用 | 不推荐 | 推荐 |
4. 特殊比较规则
(1) null
和 undefined
==
:null == undefined
返回true
。===
:null === undefined
返回false
。
(2) NaN
NaN
与任何值比较(包括自身)都返回false
。javascriptconsole.log(NaN == NaN); // false console.log(NaN === NaN); // false
(3) 对象比较
==
和===
:比较的是引用地址,而非内容。javascriptconst obj1 = {}; const obj2 = {}; console.log(obj1 == obj2); // false console.log(obj1 === obj2); // false
5. 使用建议
- 优先使用
===
:避免类型转换导致的意外结果,代码更安全、可读性更高。 - 仅在明确需要类型转换时使用
==
:例如判断变量是否为null
或undefined
。javascriptif (value == null) { // 等同于 value === null || value === undefined }
6. 示例对比
(1) 数字与字符串
console.log(0 == '0'); // true(字符串 '0' 转换为数字 0)
console.log(0 === '0'); // false(类型不同)
(2) 布尔值与其他类型
console.log(false == '0'); // true(字符串 '0' 转换为数字 0,false 也转换为 0)
console.log(false === '0'); // false(类型不同)
(3) 对象与原始值
console.log([] == 0); // true(空数组转换为空字符串 '',再转换为数字 0)
console.log([] === 0); // false(类型不同)
总结
场景 | 使用 == | 使用 === |
---|---|---|
类型检查 | 允许类型转换 | 不允许类型转换 |
安全性 | 可能导致意外结果 | 更安全,避免意外结果 |
推荐使用 | 仅在明确需要类型转换时使用 | 优先使用 |
理解 ==
和 ===
的区别有助于编写更健壮、可维护的 JavaScript 代码。
如何从浏览器的URL中获取查询字符串参数
从浏览器的 URL 中获取查询字符串参数是 Web 开发中的常见需求。以下是几种实现方法,涵盖原生 JavaScript 和现代 API 的使用。
1. 使用 URLSearchParams
(推荐)
URLSearchParams
是一个现代 API,专门用于处理 URL 查询字符串。
(1) 获取所有参数
const urlParams = new URLSearchParams(window.location.search);
// 遍历所有参数
for (const [key, value] of urlParams.entries()) {
console.log(`${key}: ${value}`);
}
(2) 获取单个参数
const paramValue = urlParams.get('paramName');
console.log(paramValue); // 输出参数值,若不存在则返回 null
(3) 检查参数是否存在
if (urlParams.has('paramName')) {
console.log('参数存在');
}
(4) 示例
假设 URL 为:https://example.com/page?name=John&age=30
const urlParams = new URLSearchParams(window.location.search);
console.log(urlParams.get('name')); // 输出:John
console.log(urlParams.get('age')); // 输出:30
2. 使用原生 JavaScript
如果不支持 URLSearchParams
,可以使用原生 JavaScript 解析查询字符串。
(1) 解析查询字符串
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
const params = getQueryParams();
console.log(params.name); // 输出:John
console.log(params.age); // 输出:30
3. 使用正则表达式
如果需要更灵活的处理,可以使用正则表达式提取参数。
(1) 提取单个参数
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
console.log(getQueryParam('name')); // 输出:John
console.log(getQueryParam('age')); // 输出:30
4. 使用第三方库
如果需要处理复杂的 URL 或兼容性要求较高,可以使用第三方库,如:
- qs:支持嵌套对象和数组的解析。
- query-string:功能强大,支持多种格式。
(1) 使用 query-string
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 中的一种简洁的条件语句,用于根据条件返回不同的值。它的语法如下:
条件 ? 表达式1 : 表达式2
- 条件:一个布尔表达式,结果为
true
或false
。 - 表达式1:如果条件为
true
,返回该表达式的值。 - 表达式2:如果条件为
false
,返回该表达式的值。
1. 基本用法
三元表达式可以替代简单的 if-else
语句,使代码更简洁。
(1) 示例
const age = 20;
const message = age >= 18 ? '成年人' : '未成年人';
console.log(message); // 输出:成年人
(2) 等价于 if-else
let message;
if (age >= 18) {
message = '成年人';
} else {
message = '未成年人';
}
2. 嵌套三元表达式
三元表达式可以嵌套使用,但过度嵌套会降低代码可读性。
(1) 示例
const score = 85;
const grade = score >= 90 ? 'A' :
score >= 80 ? 'B' :
score >= 70 ? 'C' : 'D';
console.log(grade); // 输出:B
(2) 等价于 if-else if-else
let grade;
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else if (score >= 70) {
grade = 'C';
} else {
grade = 'D';
}
3. 使用场景
(1) 赋值
const isLoggedIn = true;
const welcomeMessage = isLoggedIn ? '欢迎回来!' : '请登录';
console.log(welcomeMessage); // 输出:欢迎回来!
(2) 返回值
function getFee(isMember) {
return isMember ? '$2.00' : '$10.00';
}
console.log(getFee(true)); // 输出:$2.00
(3) 条件渲染(如 React)
const isAdmin = true;
const button = isAdmin ? <AdminButton /> : <UserButton />;
4. 注意事项
可读性:避免过度嵌套三元表达式,否则会降低代码可读性。
javascript// 不推荐 const result = condition1 ? value1 : condition2 ? value2 : condition3 ? value3 : value4;
性能:三元表达式与
if-else
性能相近,选择时以可读性为主。副作用:避免在三元表达式中执行有副作用的操作(如赋值、函数调用)。
javascript// 不推荐 const value = condition ? (a = 10) : (b = 20);
总结
特性 | 三元表达式 | if-else |
---|---|---|
语法 | 条件 ? 表达式1 : 表达式2 | if (条件) { ... } else { ... } |
简洁性 | 更简洁 | 较冗长 |
可读性 | 适合简单条件 | 适合复杂逻辑 |
嵌套 | 可嵌套,但不宜过多 | 可嵌套,适合复杂条件 |
三元表达式是 JavaScript 中一种简洁的条件语句,适合处理简单的条件逻辑,但需注意代码可读性。
JavaScript中use strict是什么,使用他的优缺点
use strict
是 JavaScript 中的一种 严格模式,用于在更严格的条件下执行代码。它可以帮助开发者避免常见的错误,并提高代码的安全性和性能。
1. 什么是 use strict
?
- 定义:
use strict
是一种指令,用于启用 JavaScript 的严格模式。 - 作用:在严格模式下,JavaScript 引擎会对代码进行更严格的解析和错误检查。
- 启用方式:
- 全局启用:在脚本文件或
<script>
标签的开头添加"use strict";
。 - 局部启用:在函数内部添加
"use strict";
。
- 全局启用:在脚本文件或
2. 严格模式的主要特性
(1) 禁止意外创建全局变量
- 非严格模式下,未声明的变量会被隐式创建为全局变量。
- 严格模式下会抛出错误。
示例:
"use strict";
x = 10; // 报错:ReferenceError: x is not defined
(2) 禁止删除不可删除的属性
- 非严格模式下,删除不可删除的属性(如
delete Object.prototype
)会静默失败。 - 严格模式下会抛出错误。
示例:
"use strict";
delete Object.prototype; // 报错:TypeError: Cannot delete property 'prototype' of Object
(3) 禁止重复的参数名
- 非严格模式下,函数允许重复的参数名。
- 严格模式下会抛出错误。
示例:
"use strict";
function sum(a, a, c) { // 报错:SyntaxError: Duplicate parameter name not allowed in this context
return a + a + c;
}
(4) 禁止使用 with
语句
with
语句会导致代码难以理解和优化。- 严格模式下会抛出错误。
示例:
"use strict";
with (Math) { // 报错:SyntaxError: Strict mode code may not include a with statement
console.log(PI);
}
(5) 禁止 this
指向全局对象
- 非严格模式下,函数中的
this
默认指向全局对象(如window
)。 - 严格模式下,
this
为undefined
。
示例:
"use strict";
function test() {
console.log(this); // 输出:undefined
}
test();
(6) 禁止八进制字面量
- 非严格模式下,以
0
开头的数字会被解析为八进制。 - 严格模式下会抛出错误。
示例:
"use strict";
const num = 0123; // 报错:SyntaxError: Octal literals are not allowed in strict mode
3. 使用 use strict
的优点
- 减少错误:避免隐式创建全局变量、意外删除属性等常见错误。
- 提高安全性:防止不安全的操作(如
eval
修改作用域)。 - 优化性能:帮助 JavaScript 引擎更好地优化代码。
- 更好的代码质量:强制使用更严格的语法,提升代码可维护性。
4. 使用 use strict
的缺点
- 兼容性问题:旧版浏览器可能不支持严格模式。
- 代码迁移成本:将非严格模式代码迁移到严格模式可能需要大量修改。
- 学习成本:开发者需要了解严格模式的规则和限制。
5. 如何启用严格模式
(1) 全局启用
在脚本文件或 <script>
标签的开头添加 "use strict";
。
"use strict";
// 严格模式下的代码
(2) 局部启用
在函数内部添加 "use strict";
。
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
。
示例:
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
。
示例:
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
。javascriptconst value = parseInt('abc'); if (Number.isNaN(value)) { console.log('解析失败,值为 NaN'); }
(2) 检查非数字值
- 如果需要检查一个值是否为数字,可以使用
isNaN
结合类型检查。javascriptfunction 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 | 先转换类型,再检查是否为NaN | isNaN('Hello') → true | 不推荐 |
Number.isNaN | 严格检查是否为NaN ,不转换类型 | Number.isNaN('Hello') → false | 推荐 |
- 推荐使用
Number.isNaN
:避免类型转换带来的意外行为。 - 避免直接使用
isNaN
:除非明确需要类型转换的逻辑。
JavaScript中负无穷大是什么
在 JavaScript 中,负无穷大(Negative Infinity) 是一个特殊的数值,表示比任何有限数都小的值。它是全局对象 Number
的一个属性,通常用于表示数学运算中的下溢或无限小的概念。
1. 负无穷大的表示
- 字面量:
-Infinity
- 属性:
Number.NEGATIVE_INFINITY
示例:
console.log(-Infinity); // 输出:-Infinity
console.log(Number.NEGATIVE_INFINITY); // 输出:-Infinity
2. 负无穷大的产生
负无穷大通常由以下操作产生:
(1) 数学运算:
- 除以 0 的负数。
- 超出数值范围的下限。
示例:
console.log(-1 / 0); // 输出:-Infinity
console.log(Number.MIN_VALUE / 0); // 输出:-Infinity
(2) 显式赋值:
const negativeInfinity = -Infinity;
console.log(negativeInfinity); // 输出:-Infinity
3. 负无穷大的特性
(1) 比较
- 负无穷大比任何有限数都小。
- 负无穷大等于自身。
示例:
console.log(-Infinity < -1000000); // 输出:true
console.log(-Infinity === -Infinity); // 输出:true
(2) 数学运算
- 负无穷大参与的运算结果通常为负无穷大或
NaN
。
示例:
console.log(-Infinity + 10); // 输出:-Infinity
console.log(-Infinity * 2); // 输出:-Infinity
console.log(-Infinity / -Infinity); // 输出:NaN
(3) 类型
- 负无穷大的类型是
number
。
示例:
console.log(typeof -Infinity); // 输出:number
4. 负无穷大的应用场景
(1) 初始化最小值
- 在查找最小值时,可以用负无穷大作为初始值。
示例:
let max = -Infinity;
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(num => {
if (num > max) {
max = num;
}
});
console.log(max); // 输出:5
(2) 数学计算
- 用于表示无限小的概念。
示例:
function calculateLimit() {
return -Infinity;
}
console.log(calculateLimit()); // 输出:-Infinity
总结
特性 | 描述 | 示例 |
---|---|---|
表示 | -Infinity 或 Number.NEGATIVE_INFINITY | console.log(-Infinity) |
比较 | 比任何有限数都小 | -Infinity < -1000000 → true |
运算 | 通常结果为-Infinity 或 NaN | -Infinity + 10 → -Infinity |
类型 | number | typeof -Infinity → number |
负无穷大是 JavaScript 中表示无限小的特殊值,常用于数学计算和初始化场景。理解其特性有助于更好地处理数值运算。
JavaScript中NaN
在 JavaScript 中,NaN 是一个特殊的数值,表示 “Not a Number”(非数字)。它通常用于表示数学运算中无法表示的结果,例如无效的算术运算或无法解析的字符串。
1. NaN 的特性
(1) 类型
NaN
的类型是number
。javascriptconsole.log(typeof NaN); // 输出:number
(2) 不等于自身
NaN
是 JavaScript 中唯一一个不等于自身的值。javascriptconsole.log(NaN === NaN); // 输出:false
(3) 全局属性
NaN
是全局对象的一个属性,可以通过NaN
或Number.NaN
访问。javascriptconsole.log(NaN); // 输出:NaN console.log(Number.NaN); // 输出:NaN
2. NaN 的产生
NaN
通常由以下操作产生:
(1) 无效的数学运算:
- 0 除以 0。
- 无穷大除以无穷大。
- 负数开平方。
示例:
console.log(0 / 0); // 输出:NaN
console.log(Infinity / Infinity); // 输出:NaN
console.log(Math.sqrt(-1)); // 输出:NaN
(2) 无法解析的字符串:
- 使用
parseInt
或parseFloat
解析非数字字符串。
示例:
console.log(parseInt('Hello')); // 输出:NaN
console.log(parseFloat('abc')); // 输出:NaN
(3) 显式赋值:
const result = NaN;
console.log(result); // 输出:NaN
3. 检测 NaN
由于 NaN
不等于自身,因此不能直接使用 ===
或 ==
检测。以下是几种检测 NaN
的方法:
(1) 使用 isNaN
函数
- 问题:
isNaN
会先将参数转换为数字,再检查是否为NaN
。 - 行为:对于非数字的值(如字符串),
isNaN
也会返回true
。
示例:
console.log(isNaN(NaN)); // 输出:true
console.log(isNaN('Hello')); // 输出:true(字符串 'Hello' 转换为数字失败,返回 NaN)
(2) 使用 Number.isNaN
函数
- 改进:
Number.isNaN
严格检查一个值是否为NaN
,不会进行类型转换。
示例:
console.log(Number.isNaN(NaN)); // 输出:true
console.log(Number.isNaN('Hello')); // 输出:false(字符串,不是 NaN)
(3) 使用 Object.is
方法
- 作用:严格比较两个值是否相同,包括
NaN
。
示例:
console.log(Object.is(NaN, NaN)); // 输出:true
4. 处理 NaN
(1) 检查并处理
- 在数学运算后检查结果是否为
NaN
,并进行相应处理。
示例:
const result = Math.sqrt(-1);
if (Number.isNaN(result)) {
console.log('计算结果无效');
}
(2) 默认值
- 使用
||
或??
运算符为NaN
提供默认值。
示例:
const value = parseInt('abc') || 0; // 如果解析失败,返回 0
console.log(value); // 输出:0
总结
特性 | 描述 | 示例 |
---|---|---|
类型 | number | typeof NaN → number |
比较 | 不等于自身 | NaN === NaN → false |
检测 | 使用Number.isNaN 或 Object.is | Number.isNaN(NaN) → true |
产生原因 | 无效的数学运算或无法解析的字符串 | 0 / 0 → NaN |
NaN
是 JavaScript 中表示无效数值的特殊值,理解其特性和检测方法有助于更好地处理数学运算中的异常情况。
JavaScript展开运算符
展开运算符(Spread Operator) 是 JavaScript 中的一种语法,使用 ...
表示。它可以将数组、对象或字符串“展开”为独立的元素,常用于函数调用、数组操作和对象操作。
1. 展开运算符的基本用法
(1) 展开数组
- 将数组展开为独立的元素。
示例:
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // 输出:[1, 2, 3, 4, 5]
(2) 展开对象
- 将对象展开为独立的键值对。
示例:
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };
console.log(obj2); // 输出:{ a: 1, b: 2, c: 3 }
(3) 展开字符串
- 将字符串展开为独立的字符。
示例:
const str = 'hello';
const chars = [...str];
console.log(chars); // 输出:['h', 'e', 'l', 'l', 'o']
2. 展开运算符的应用场景
(1) 函数调用
- 将数组展开为函数的参数。
示例:
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 输出:6
(2) 数组操作
- 合并数组。
- 复制数组。
示例:
// 合并数组
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) 对象操作
- 合并对象。
- 复制对象。
示例:
// 合并对象
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) 字符串操作
- 将字符串转换为数组。
示例:
const str = 'hello';
const chars = [...str];
console.log(chars); // 输出:['h', 'e', 'l', 'l', 'o']
3. 展开运算符的注意事项
(1) 浅拷贝:
- 展开运算符只能进行浅拷贝,嵌套对象或数组不会被深拷贝。
const original = { a: { b: 1 } };
const copy = { ...original };
copy.a.b = 2;
console.log(original.a.b); // 输出:2(原对象也被修改)
(2) 对象展开的顺序:
- 如果对象中有重复的键,后面的值会覆盖前面的值。
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 等)。
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) 语法
try {
// 可能抛出错误的代码
} catch (error) {
// 捕获并处理错误
} finally {
// 无论是否抛出错误,都会执行的代码
}
(2) 各部分作用
try
:包裹可能抛出错误的代码。catch
:捕获错误并处理。finally
:无论是否抛出错误,都会执行的代码(常用于清理资源)。
(3) 示例
try {
const result = 10 / 0;
if (!isFinite(result)) {
throw new Error('计算结果无效');
}
} catch (error) {
console.log('捕获错误:', error.message); // 输出:捕获错误:计算结果无效
} finally {
console.log('执行完毕');
}
2. 抛出异常
(1) throw
语句
- 用于手动抛出异常。
- 可以抛出任何类型的值(如字符串、数字、对象等),但通常抛出
Error
对象。
示例:
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
创建自定义错误类型。
示例:
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
检查错误类型。
示例:
try {
throw new TypeError('类型错误');
} catch (error) {
if (error instanceof TypeError) {
console.log('捕获类型错误:', error.message);
} else {
console.log('捕获其他错误:', error.message);
}
}
(2) 避免空的 catch
块
- 空的
catch
块会隐藏错误,导致难以调试。
错误示例:
try {
throw new Error('错误');
} catch (error) {
// 空的 catch 块
}
正确示例:
try {
throw new Error('错误');
} catch (error) {
console.log('捕获错误:', error.message);
}
(3) finally
块的作用
finally
块中的代码无论是否抛出错误都会执行。- 常用于释放资源(如关闭文件、清理定时器等)。
示例:
let resource = null;
try {
resource = allocateResource();
useResource(resource);
} catch (error) {
console.log('捕获错误:', error.message);
} finally {
if (resource) {
releaseResource(resource);
}
}
4. 异步代码中的异常处理
(1) Promise 的异常处理
- 使用
.catch()
捕获 Promise 中的错误。
示例:
fetch('https://example.com')
.then(response => response.json())
.catch(error => {
console.log('捕获错误:', error.message);
});
(2) async/await
的异常处理
- 使用
try...catch
捕获async
函数中的错误。
示例:
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...catch | async function() { try { ... } catch { ... } } |
异常处理是 JavaScript 中保证程序健壮性的重要机制,合理使用 try...catch...finally
可以有效捕获和处理错误,避免程序崩溃。
JavaScript如何删除属性及其值
在JavaScript中,可以使用delete
操作符来删除对象的属性及其值。delete
操作符会从对象中移除指定的属性,如果删除成功,返回true
;如果属性不存在或无法删除,返回false
。
示例代码
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" }
注意事项
不可配置的属性:如果属性是不可配置的(即
configurable
为false
),delete
操作符无法删除该属性,并且会返回false
。javascriptlet obj = {}; Object.defineProperty(obj, 'name', { value: 'Alice', configurable: false }); let result = delete obj.name; console.log(result); // 输出: false console.log(obj.name); // 输出: "Alice"
全局变量和函数:
delete
操作符不能删除使用var
、let
或const
声明的变量或函数。javascriptvar x = 10; console.log(delete x); // 输出: false function foo() {} console.log(delete foo); // 输出: false
数组元素:虽然可以使用
delete
删除数组元素,但这会在数组中留下一个undefined
的空位,而不会改变数组的长度。javascriptlet arr = [1, 2, 3]; delete arr[1]; console.log(arr); // 输出: [1, undefined, 3] console.log(arr.length); // 输出: 3
总结
delete
操作符用于删除对象的属性。- 如果属性不存在或无法删除,
delete
会返回true
。 delete
不能删除使用var
、let
或const
声明的变量或函数。- 删除数组元素会留下
undefined
的空位,不会改变数组长度。