JS基础大乱炖一锅出
JS基础大乱炖一锅出
1、new操作符的实现原理
原理!就4步!
1、创建空对象
2、把新对象内部的[[Prototype]]指向构造函数的prototype
3、以新对象为this调用构造函数,初始化新对象
4、如果构造函数返回的是对象,直接返回。否则,返回创建的新对象。
1 |
|
2、map和Object的区别
map和object都能够存储键值对,那好奇的朋友就发问了,有什么区别呢?
有的。
1、键的类型
Map:什么类型都可以,字符串√ 数字√ 对象√函数√ NaN也行!并且严格区分,不会自动转换为字符串。
1
2
3
4
5const map = new Map();
map.set(1, "number one");
map.set("1", "string one");
console.log(map.get(1)); // "number one"
console.log(map.get("1")); // "string one"Object:只能是字符串或者Symbol,不是字符串的都会自动转换为字符串!
1
2
3
4const obj = {};
obj[1] = "number one"; // 数字 1 会转换为字符串 "1"
obj["1"] = "string one"; // 覆盖"1"的值
console.log(obj[1]); // "string one"
2、键的顺序
- Map:内部会保存插入顺序,遍历的时候也是按照插入顺序返回的
- Object:ES6 规范中规定对象属性遍历顺序,但实际行为在不同情况下可能不一致
3、大小属性
- Map:获取键值对数量使用
map.size
- Object:没有直接获取键值对数量的方法,可以使用
Object.keys(obj).length
4、迭代方面
Map:可以迭代,使用
for...of
循环、map.forEach()
或解构语法1
2
3
4const map = new Map([["a", 1], ["b", 2]]);
for (const [key, value] of map) {
console.log(key, value);
}Object:本身是不可迭代的。需要转换成键数组、值数组或键值对数组。
Object.keys()
、Object.values()
或Object.entries()
。1
2
3
4const obj = { a: 1, b: 2 };
for (const key of Object.keys(obj)) {
console.log(key, obj[key]);
}
5、应用场景
- Map:更适合频繁增加和删除键值对的场景。没有原型链干扰,它不继承来自
Object.prototype
的属性。 - Object:通常用于存储结构化数据或作为简单的字典,但可能会受到原型链的干扰(例如内置属性或方法)。
3、Map和WeakMap的区别
键的类型
- Map:键可以是任意类型
- WeakMap:键只能是对象!只能是对象!对象对象对象~
可枚举性
- Map:可迭代,使用
for...of
、map.forEach()
,也有size
属性 - WeakMap:不可迭代!没有遍历方法!也没有
size
属性!不允许列举键值对,因为它的键是弱引用,随时都可能被回收~
使用场景
- Map:频繁增加、删除、遍历键值对的场景、用于数据持久存储和操作,或者需要保证键的顺序。
- WeakMap:存储与对象相关的元数据或私有数据。当希望在对象不再被使用时,该对象对应的相关数据能自动被清除,使用 WeakMap 可以有效防止内存泄漏。
4、JavaScript有哪些内置对象
“内置对象”是指 JavaScript 语言在其标准中预先定义并由 JavaScript 引擎自动提供的对象。
全局对象:存储全局可用的变量和函数。
在浏览器中,全局对象通常是
window
(或self
,例如在 Web Worker 中)。在 Node.js 中,全局对象通常是
global
。为了统一跨平台访问,ES2020 引入了
globalThis
,它提供了一个标准的方式来引用全局对象。
基本类型包装对象:Object
、Function
、Number
、String
、Boolean
、Symbol
、BigInt
等。
集合和数组相关:Array
、Map
、Set
、WeakMap
、WeakSet
。
工具对象:Math
(数学函数和常量)、JSON
(解析和字符串化 JSON 数据)。
日期和正则:Date
、RegExp
。
错误处理:Error
及其派生类型(如 TypeError
、SyntaxError
等)。
二进制数据处理:ArrayBuffer
、DataView
、各类 TypedArray
。
异步编程:Promise
等。
反射与代理:Reflect
、Proxy
。
5、常用的正则表达式有哪些
匹配数字:该字符串从开始到结束全部由一个或多个数字组成。
1 |
|
匹配英文:确保整个字符串只包含一个或多个英文字母
1 |
|
匹配邮箱:
1 |
|
匹配号码:
1 |
|
6、对JSON的理解
JSON 是一种文本格式,可以方便地在不同编程语言之间传输数据。
JSON 是 Web 开发中最常用的数据交换格式。
客户端与服务器之间通常使用 JSON 格式传输数据,因为它格式简单、易于解析并且与多种编程语言兼容。
严格的语法规则
- 键必须用双引号括起来,单引号不合法。
- 字符串也必须使用双引号。
- JSON 不允许使用尾随逗号。
数据转换
序列化
将 JavaScript 对象转换为 JSON 字符串可以使用JSON.stringify()
方法:1
2
3const obj = { name: "Alice", age: 25 };
const jsonString = JSON.stringify(obj);
console.log(jsonString); // 输出: '{"name":"Alice","age":25}'反序列化
将 JSON 字符串解析为 JavaScript 对象可以使用JSON.parse()
方法:1
2
3const jsonString = '{"name":"Alice","age":25}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // 输出: "Alice"
7、JavaScript脚本延迟加载的方式有哪些
延迟加载(Lazy Loading) 是一种优化技术,目的是推迟非关键脚本的加载和执行,从而减少页面初始加载时间、提升首屏渲染速度,并降低不必要的资源消耗。
1、使用defer
加载时机:立即开始加载(下载)
执行时机:HTML文档解析完成后,DOMContentLoaded事件触发前,按出现顺序执行。
特点:不会阻塞页面的解析加载,保证脚本执行顺序和文档中出现的顺序一致。
1 |
|
2、使用async
加载时机:立即开始加载(下载)(异步加载)
执行时机:加载完成,就立即执行
特点:不保证顺序,取决于它们的加载速度,不会阻塞页面解析和渲染
1 |
|
3、动态插入脚本 createElement
通过JS动态创建<script>
标签,插入到DOM中,从而延迟加载脚本。
灵活,可以在特定条件或事件发生时加载脚本。
1 |
|
**4、动态加载模块 import() **
使用 ES6 提供的动态 import()
方法,在需要时再异步加载模块。
返回一个 Promise,可以通过 .then()
或 await
处理加载结果。
1 |
|
5、脚本置底
将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
8、JavaScript 类数组对象的定义
类数组对象:是对象,和数组具有类似的数组结构,但不是真正的Array对象。
- 能够通过索引访问元素
- 有length属性
- 不一定继承Array.prototype
常见的类数组对象
arguments
对象、DOM 集合如NodeList
HTMLCollection
、字符串
1 |
|
类数组对象转换为数组呢
Array.from方法
1
const argsArray = Array.from(arguments);
扩展运算符
1
const argsArray = [...arguments]
Array.prototype.slice
1
const argsArray = Array.prototype.slice.call(arguments);
9、数组有哪些原生方法
修改:push、pop、shift、unshift、splice、sort、reverse 、fill【影响原数组】
生成新数组:concat、slice、flat、flatMap、join(生成的字符串)
迭代与处理:forEach(返回 undefined
,可以主动修改原数组)、map(返回新数组)、filter、reduce、every、some、find、findIndex
搜索:indexOf、lastIndexOf、includes
迭代器相关:keys、values、entries
10、Unicode、UTF-8、UTF-16、UTF-32的区别
Unicode:国际标准,抽象的字符集,世界上几乎所有的文字和符号都分配了唯一的数字标识(代码点)。旨在取代各种字符编码系统,实现全球字符的统一表示。
Unicode 的代码点范围从
U+0000
到U+10FFFF
UTF是为了实现Unicode字符集的存储和传输的一系列编码方案,它们都把Unicode的代码点转换为一系列字节,但是具体方式和效率不同。
UTF-8:
- 可变长度(1~4 字节)。
- 向后兼容 ASCII,节省英文文本空间,广泛应用于网络和文件存储。
UTF-16:
- 可变长度(2 或 4 字节)。
- 大多数字符用 2 字节表示,超出基本平面的字符用 4 字节。
- 被 Windows 和 JavaScript 等使用。
UTF-32:
- 固定长度(4 字节)。
- 编码简单,但内存占用大,使用较少。
11、常见的位运算符有哪些?其计算规则是什么
位运算符:直接操作数字的二进制位。
JavaScript 中的数字是以 64 位浮点数(IEEE 754 格式)存储的,但在进行位运算时,它们会被暂时转换为 32 位有符号整数。位运算符通常比算术运算符更快,因为它们直接操作内存中的二进制位。
**按位与&**:对应的二进制位都是1,则该位结果是1,否则是0
1 |
|
按位或|:对应的二进制位只要有1,则该位结果是1,否则是0
1 |
|
按位异或^:对应的二进制位,只有两个操作数都不同,则该位结果是1,否则是0
1 |
|
**按位非~**:对每一位取反(对 32 位整数取反后再解释成带符号数)
1 |
|
**左移运算符<<**:二进制左移指定的位数,补0
1 |
|
**有符号右移运算符>>**:二进制右移指定的位数,左侧根据原来的符号补(正数补0,负数补1)
1 |
|
**无符号右移运算符>>>**:将一个数的二进制位向右移动指定的位数,左侧始终补 0,不考虑符号位,
1 |
|
12、为什么函数的 arguments 参数是类数组而不是数组?如何遍历类数组
arguments是类数组,它被设计成一个简单的对象,仅提供数字索引和 length
属性。
在许多使用场景中,开发者只需要读取参数数量和逐个访问参数,而不需要数组提供的高级方法,因此将其设计为类数组就足够了。
传统的for循环
1 |
|
for ..of循环
1 |
|
转换为真正的数组
使用 Array.from()
:
1 |
|
使用扩展运算符:
1 |
|
使用 Array.prototype.slice.call()
:
1 |
|
13、什么是 DOM 和 BOM?
DOM:文档对象模型 – Document Object Model
是一种标准的编程接口,代表整个文档的结构,是一个树形结构,用来表示HTML文档的结构和内容。它把页面解析为一个树状结构,每个HTML元素、属性、文本都被视为一个节点。允许脚本动态访问和修改页面的内容、结构、样式。提供丰富的 API 以实现动态交互和更新页面内容。
元素选择
document.getElementById(id)
document.getElementsByClassName(className) ———– HTMLCollection 动态
document.getElementsByTagName(tagName) ———– HTMLCollection 动态
document.querySelector(selector)
document.querySelectorAll(selector) ——– NodeList 静态
节点操作
document.createElement(tagName)
element.appendChild(node)
element.insertBefore(newNode, referenceNode)
element.removeChild(node)
element.replaceChild(newNode, oldNode)
element.cloneNode(deep)
内容和属性操作
element.innerHTML
element.textContent
element.setAttribute(name, value)
element.getAttribute(name)
element.removeAttribute(name)
样式与类操作
element.style
element.classList.add()/remove()/toggle()/contains()
事件处理
element.addEventListener(event, handler, options)
element.removeEventListener(event, handler, options)
element.dispatchEvent(event)
DOM 遍历
node.parentNode / node.parentElement
node.childNodes / element.children
node.firstChild / node.lastChild
node.nextSibling / node.previousSibling
BOM:浏览器对象模型 – Browser Object Model
是和浏览器交互的对象和接口,主要用来访问和控制浏览器窗口和环境,而不是页面内容本身。允许开发者操作浏览器窗口、导航、历史记录、屏幕信息等。包括 window
、navigator
、location
、history
、screen
等对象。不同于 DOM,它不规定文档内容的结构,而是与浏览器环境(例如窗口、框架、地址栏等)相关。
window 对象
window.alert(message)
window.confirm(message)
window.prompt(message, default)
window.open(url, name, specs)
window.close()
window.setTimeout(callback, delay)
window.setInterval(callback, delay)
window.clearTimeout(id) / window.clearInterval(id)
location 对象
location.href
location.assign(url)
location.replace(url)
location.reload()
history 对象
history.back()
history.forward()
history.go(delta)
navigator 对象
navigator.userAgent
navigator.language
navigator.onLine
navigator.geolocation
screen 对象
screen.width、screen.height
screen.availWidth、screen.availHeight
14、对AJAX的理解,实现一个AJAX请求
AJAX(Asynchronous JavaScript and XML)是一种技术,不用重新加载整个页面,能够与服务器交换数据,来更新部分网页的技术。网页可以实现异步加载数据,从而改善用户体验。虽然名字里有XML,但是现代AJAX请求通常使用JSON格式的数据进行交互。
- 异步通信:在后台与服务器通信,不会阻塞用户界面或者刷新页面。
- 数据格式:最初是XML,现在是JSON。
- 浏览器支持:可以使用XMLHttpRequest对象或者fetch API。
实现一个 AJAX 请求(使用 XMLHttpRequest)
1、创建XMLHttpRequest对象
2、配置请求
3、设置状态变化监听
4、发送请求
5、回调处理
1 |
|
? fetch怎么使用
xhr需要手动检查readyState
和status
判断请求是否成功,fetch是基于Promise的,语法更简洁,便于链式调用和错误捕获。
1 |
|
? 使用 Promise 封装 AJAX
1 |
|
xhr.readyState:0,1,2,3,4
15、JavaScript为什么要进行变量提升
变量提升:代码执行前,解释器会先扫描整个作用域(函数作用域或者全局作用域),将变量和函数的声明都提升到作用域的最前面。这里的提升针对的是声明部分,不是赋值。
为什么会有变量提升
JS在执行代码前,会先经过一个编译阶段。在这个阶段,解释器会给每个变量和函数创建内存空间,并且建立作用域链。在 ES5 及之前的版本中,JavaScript 只有函数级作用域(没有块级作用域)。为了保持作用域的一致性,变量声明都会被提升到函数或全局的最顶部,这样可以确保在整个函数体内都能访问到这些变量(虽然如果在使用前没有赋值,则值为 undefined
)。变量提升是 JavaScript 早期设计的一部分,它帮助解释器在运行时对代码结构有一个整体的认识。虽然这可能会让初学者感到困惑,但它是语言实现的一种选择,同时也为后续的改进(比如 ES6 中的 let
和 const
,它们也存在提升但有“暂时性死区”)打下了基础。
16、ES6模块与CommonJS模块有什么异同
它们都是JS里面的模块化系统,用于实现代码的分离、组织和重用。
ES Module:是ECMAScript2015(ES6)引入的官方模块系统,已经是JS标准的一部分。模块的依赖关系在编译时就能确定。语法是通过export
导出变量或者函数,通过import
引入其他模块。
1 |
|
CommonJS:是Node.js中使用的模块系统,适合服务器端环境。模块在加载时采用同步加载,在运行时解析依赖,适合于服务器环境的同步 I/O 操作。语法是通过module.exports
或者exports
导出。通过require
引入模块。
1 |
|
它们的差异:
语法差异
es6模块:使用
export
、export default
关键字导出,import
导入。1
2
3
4
5// 命名导出
export const foo = 'bar';
export function func() { ... };
//默认导出 只能有一个
export default function() { ... };1
2
3
4//导入命名导出 解构语法
import { foo, func } from './module.js';
//导入默认导出
import myFunc from './module.js';CommonJS:使用
module.exports
或者exports
导出,require
导入。exports
是module.exports
的简写,只能用于添加属性。1
2
3
4
5// 导出 只能有一个
module.exports = { value: 42 };
// 导出多个命名成员
exports.foo = "Hello";
exports.bar = "World";1
const moduleA = require('./moduleA');
加载和执行的时机
es6:静态加载,编译时就确定模块依赖关系,可以进行静态分析。支持异步加载。
commonjs:同步加载,动态加载,
require()
可出现在代码任意位置。在require
调用时,会立即加载并执行对应模块。适用服务器端,因为文件IO操作通常是同步的。作用域与变量绑定
es6:实时绑定。导出的是值的引用,模块内部变化会同步到导入方。当模块内部的值变化时,导入的模块可以感知变化。自动采用严格模式,提高代码健壮性。导入的是实时的引用(live binding),看到的是原始变量的当前状态;这种引用是只读的(不能整体重赋值),但如果值是对象,修改内部属性仍然可行。
commonjs:模块内部的变量是私有的,通过
require
的引入其实是模块导出对象的拷贝。一旦加载完成,再改变内部变量,不会反映在其他模块中导入的值上。导入时类似“浅拷贝”:拿到的是一个快照,如果整个对象被重新赋值,导入的绑定不会改变,但内部属性修改仍然共享。循环依赖的处理
es6:依赖关系在编译阶段就已确定,采用实时绑定,可以在模块间动态同步状态。
但要注意,在模块尚未完全初始化前使用导入的变量可能会出现问题,因此也需要谨慎设计模块间的调用顺序。commonjs:模块加载是同步的,缓存机制可能导致在循环依赖中获取到部分初始化的导出对象。
解决循环依赖的关键在于重新组织代码结构或延迟加载。
共同点
- 目的一致:两者都用于将代码分割成独立的模块,实现职责分离和代码重用。
- 对象属性修改:无论采用哪种模块系统,如果模块导出的是一个对象,导入后都可以修改这个对象内部的属性值(前提是对象本身没有被重新赋值)。
17、常见的DOM操作有哪些
获取元素
通过ID:document.getElementById
通过标签名: document.getElementsByTagName
通过类名:document.getElementsByClassName
通过css选择器:document.querySelector
、document.querySelectorAll
创建元素:document.createElement
插入元素:
父元素末尾插入:parentElement.appendChild
指定元素前插入:parentElement.insertBefore(newElement, referenceElement)
删除元素:
删除子元素:parentElement.removeChild
直接删除元素:element.remove()
修改元素:
修改元素内容:element.textContent
、element.innerHTML
修改元素属性:element.setAttribute
、element.getAttribute
、element.removeAttribute
修改样式:element.style.
、element.classList.add
、element.classList.remove
、element.classList.toggle
、element.classList.contains
遍历元素:
获取父元素: element.parentElement
获取子元素:element.children
、 element.firstElementChild
、element.lastElementChild
获取兄弟元素:element.nextElementSibling
、element.previousElementSibling
事件操作:
绑定事件:element.addEventListener
移除事件:element.removeEventListener
18、 use strict是什么
use strict是JS里面的一条指令,启动严格模式。能帮助写出更健壮、更安全的代码。
1 |
|
可以在脚本的最开始添加,或者在函数内部启用。
特点:
捕获错误:比如使用未声明的变量会报错 // ReferenceError: a is not defined
在非严格模式下,直接给未声明的变量赋值,会自动创建一个全局变量;
普通函数调用,this 关键字的绑定:
非严格模式,默认指向全局对象(window)
严格模式,则为undefined。迫使开发者在使用 this
时更加明确其绑定对象,减少潜在的错误。
禁止重复的参数名和对象属性
禁止删除变量、函数或参数
禁用 with 语句(
with
语句会改变作用域链,容易引起混乱和难以调试的问题)部分标识符被视为保留字,不能用作变量名或函数名。例如
public
19、如何判断一个对象是否属于某个类
1、使用instance of运算符
检查对象的原型链里面是否包含某个构造函数的prototype对象。
instance of 的原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function myInstanceof(object, constructor) {
if (typeof object !== "object" || object === null) {
return false;
}
let proto = Object.getPrototypeOf(object);
while (proto) {
if (proto === constructor.prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
1 |
|
还有类class的情况
1 |
|
2、检查原型链
1 |
|
3、比较constructor属性
每个对象都有一个constructor属性,指向创建对象的构造函数。
但这种方法可能会受到构造函数属性被修改的影响,所以不如 instanceof
稳定。
1 |
|
20、强类型语言和弱类型语言的区别
强类型语言:变量和表达式都有严格的类型限制,不同类型之间不能随意混用或者隐式转换,所有的类型转换必须明确的进行。这种严格性可以在编译时或者运行时捕获错误。
弱类型语言:允许自动隐式转换。代码更简洁,不需要频繁显式声明类型、转换类型。比如数值与字符串的相加。
21、解释性语言和编译型语言的区别
编译语言:(C、C++、Go )
需要通过编译器把源代码转换为可以直接执行的机器码。
可执行文件在操作系统上直接运行,不需要额外的中间解释步骤,运行速度通常较快。
1 |
|
优点:
执行效率高:编译后的机器码直接运行,省去了运行时解释的开销。
提前发现错误:编译器在编译阶段会进行语法、类型等检查,能在运行前捕获部分错误。
缺点:
- 编译时间:大规模项目可能需要较长的编译时间。
- 平台依赖性:生成的机器码通常与平台相关,不同操作系统和硬件需要不同版本的可执行文件。
- 调试不够灵活:编译后的代码和源代码之间的映射可能不直观,调试时需要借助符号表等辅助工具。
解释语言(Python、JavaScript)
在运行时由解释器逐行读取源代码,即时执行。将每一行代码翻译成机器码后马上执行。
不需要事先生成独立的可执行文件。执行速度可能较编译型语言慢一些。
1 |
|
优点:
- 跨平台性好:只要有相应平台的解释器,源代码可以直接运行,不依赖平台特定的机器码。
- 开发调试便捷:无需编译,可以快速修改代码并立即看到效果,适合快速原型开发。
- 动态性高:支持动态类型、动态绑定等特性,使得语言更灵活。
缺点:
- 执行效率较低:每次运行都需要解析源代码,存在额外的解释开销。
- 错误发现较晚:某些错误只能在运行时才会暴露,可能影响程序稳定性。
混合模式:
现代语言和执行环境往往采用编译与解释相结合的方式,例如:
- Java:先将源代码编译成平台无关的字节码,再由 Java 虚拟机(JVM)解释执行或采用即时编译(JIT)技术将热点代码编译成本地机器码。
- JavaScript:传统上是解释执行,但现代引擎(如 V8、SpiderMonkey)使用 JIT 技术提高执行效率。一旦检测到某部分代码频繁执行,引擎就会将其编译成机器码,提高后续执行速度。
22、什么是尾调用,使用尾调用有什么好处?
尾调用:在函数地方最后一步直接调用另一个函数(或者自身)。
递归调用是函数最后一步,不需要额外计算。
1 |
|
好处:
- 减少调用栈的使用:可以复用当前函数的调用帧,避免调用栈不断增长。
- 提高性能:由于不需要为每一次递归调用都分配和销毁调用帧,尾调用优化能减少内存分配和回收的开销,提升程序执行效率。
- 支持无限递归:论上无限深的递归(例如遍历一个非常大的数据结构),尾调用优化使得这种递归成为可能,因为不会导致栈溢出。
调用栈(Call Stack):管理函数调用的内存区域,以“栈”(后进先出)的结构管理所有正在执行的函数调用。
调用帧(Stack Frame):每当一个函数被调用时,程序会为该函数创建一个“调用帧”。这个帧保存了函数执行时所需的所有信息:函数参数、局部变量、返回程序地址。
调用帧确保当函数调用嵌套(包括递归)时,每个函数都有自己的执行环境。函数结束时,它对应的调用帧会从调用栈中移除,从而恢复到之前的状态。
当一个函数调用另一个函数时,就会创建新的调用帧。如果函数递归调用自己而没有进行尾调用优化,每次递归都需要分配新的帧。随着递归深度增大,调用栈上的帧也会越来越多,这可能导致内存不足(栈溢出)。
尾调用优化的核心思想是:
如果函数的最后一步是调用另一个函数(或自身),当前函数调用结束后不再需要做其他操作,那么就可以不创建新的帧,而是复用当前的帧。
23、for…in和for…of的区别
遍历对象属性:如果使用for..in,需要结合hasOwnProperty
过滤继承属性!
1 |
|
遍历数组和其他可迭代对象:推荐使用 for...of
,因为它更直观,且不会受到对象属性遍历时的顺序问题影响。普通的对象用for..of遍历是会报错的,如果需要遍历的对象是类数组对象,用Array.from转成数组即可。
24、 ajax、axios、fetch的区别
AJAX (XMLHttpRequest)
浏览器内置的 XMLHttpRequest
对象来实现前后端数据交互。基于回调函数。
原生支持:几乎所有浏览器都支持 XMLHttpRequest
。
非 Promise 化:默认 API 基于回调,代码结构较为繁琐,错误处理不够直观。
1 |
|
Fetch
ES6 后新增的浏览器原生 API,采用 Promise 方式封装 HTTP 请求。
基于 Promise:使用 Promise 链式调用,方便处理异步请求和错误。
更简洁的语法:写法直观,不需要处理复杂的状态机(如 readyState)。
错误处理需要注意:默认只有网络错误会导致 Promise 被 reject,对于 HTTP 错误(如 404、500)不会自动 reject,需要手动判断 response.ok
。
浏览器兼容性:现代浏览器支持较好,但老版本浏览器可能需要 polyfill。
1 |
|
Axios
基于 Promise 的第三方 HTTP 请求库
基于 Promise:与 Fetch 类似,代码风格现代且易于理解
自动转换 JSON:响应数据默认会自动转换成 JSON 对象,无需手动调用 .json()
。
拦截器支持:可以在请求和响应时进行预处理(如添加 token、统一处理错误)。
更丰富的配置:支持请求超时、取消请求、配置 baseURL、并发请求等。
兼容性:封装了底层实现,适用于大部分浏览器和 Node.js 环境。
1 |
|
1 |
|
25、addEventListener()方法的参数和使用
这是一个向DOM元素添加事件监听器的方法~!
当指定的事件发生时,会执行回调函数。
1 |
|
type(必选):字符串,指定要监听的事件类型,如 "click"
、"mouseover"
、"keydown"
等。
1 |
|
listener(必选):当事件发生时被调用的回调函数,函数要接收一个事件对象作为参数。
1 |
|
options(可选):
旧版用法(布尔值 useCapture):一个布尔值,指定是否在捕获阶段调用事件处理程序。如果为 true
,则在事件捕获阶段调用;默认为 false
,在冒泡阶段调用。
现代用法(options 对象):
是一个对象,可以配置多个选项:
- capture:布尔值,和
useCapture
功能一样,指定事件在捕获阶段触发。 - once:布尔值,如果为
true
,监听器在第一次触发后会自动移除。 - passive:布尔值,表示监听器永远不会调用
preventDefault()
,有助于提高滚动性能。
1 |
|
可以使用 removeEventListener
方法移除监听器
? 事件传播阶段
事件在 DOM 树中传播分为三个阶段:捕获阶段、目标阶段和冒泡阶段。
通过设置
capture
选项(或旧的useCapture
参数),可以指定事件处理程序在捕获阶段或冒泡阶段被调用。
? 三个阶段在做什么
捕获阶段:当一个事件发生时(例如点击一个按钮),浏览器会从
document
根节点开始,沿着 DOM 树从最外层的元素逐级传递事件,直到到达事件的目标元素。监听器需设置capture: true
才能在此阶段触发。目标对象:当事件到达目标元素时,进入目标阶段。在这一阶段,目标元素本身既处于捕获阶段的末尾,也处于冒泡阶段的开始。所有监听器均会触发。
冒泡阶段:事件在目标阶段处理完毕后,会从目标元素开始,沿着 DOM 树向上逐级传递回根元素(即
document
),这个过程称为冒泡阶段。这个阶段,事件会依次触发父级元素上注册的监听器。
?事件类型
- 鼠标事件
click:鼠标单击事件。
dblclick:鼠标双击事件。
mousedown:鼠标按下事件。
mouseup:鼠标释放事件。
mousemove:鼠标移动事件。
mouseover / mouseout:鼠标进入/离开元素事件。
mouseenter / mouseleave:类似于 mouseover/mouseout,但不冒泡。
contextmenu:右键菜单事件。- 键盘事件
keydown:按键按下时触发。
keyup:按键释放时触发。
keypress:按键产生字符时触发(现已逐渐废弃)。- 表单及输入事件
submit:表单提交事件。
change:表单元素的值发生变化时触发(下拉列表、复选框、文本框等)。
input:输入框内容发生变化时触发。
focus:元素获得焦点时触发。
blur:元素失去焦点时触发。- 窗口/文档事件
load:页面或图像等资源加载完成时触发。
unload:页面卸载时触发。
beforeunload:在页面卸载之前触发(常用于提示用户)。
resize:窗口尺寸改变时触发。
scroll:滚动条滚动时触发。
DOMContentLoaded:初始的 HTML 文档被完全加载和解析完成后触发。- 触摸事件(移动端)
touchstart:触摸开始时触发。
touchmove:触摸移动时触发。
touchend:触摸结束时触发。
touchcancel:触摸因某种原因中断时触发。- 指针事件(支持鼠标、触控、笔输入)
pointerdown、pointerup、pointermove、pointercancel 等。- 拖拽事件
drag、dragstart、dragenter、dragover、dragleave、drop、dragend。- 动画与过渡事件
animationstart、animationend、animationiteration。
transitionend。- 媒体事件
play、pause、ended:视频或音频播放控制。
timeupdate:媒体播放位置更新时触发。
volumechange:音量变化时触发。
canplay:足够的数据已经加载,可以开始播放时触发。
error:媒体加载或播放过程中发生错误时触发。- 其他事件
error:加载资源(图片、脚本等)失败时触发。
wheel:鼠标滚轮滚动事件。