89. 什么是闭包?
-
闭包的定义:
闭包是指一个函数可以访问它的词法作用域中的变量,即使这个函数在它的词法作用域之外被调用。换句话说,闭包使得函数可以记住并访问它所在的作用域中的变量,即使该函数在这个作用域之外执行。 -
闭包的用途:
- 数据的持久化:闭包可以用于创建私有变量或持久化状态。
- 函数工厂:可以用来创建不同作用域中的函数。
- 回调函数:在异步操作或事件处理程序中使用闭包,以保持对外部变量的访问。
-
闭包示例:
function outerFunction(outerVariable) { return function innerFunction(innerVariable) { console.log('Outer Variable:', outerVariable); console.log('Inner Variable:', innerVariable); }; } const newFunction = outerFunction('外部'); newFunction('内部');
在这个例子中,
innerFunction
是一个闭包,因为它可以访问outerFunction
的outerVariable
变量,即使outerFunction
已经执行完毕。
90. 作用域链
-
作用域链的定义:
当访问一个变量时,JavaScript 引擎会首先在当前作用域中查找该变量。如果找不到,它会沿着作用域链向外层作用域查找,直到全局作用域。如果仍未找到,则会抛出ReferenceError
。 -
作用域链的结构:
- 当前函数作用域。
- 外层函数作用域(如果有的话)。
- 全局作用域。
-
作用域链示例:
const globalVar = 'global'; function outerFunction() { const outerVar = 'outer'; function innerFunction() { const innerVar = 'inner'; console.log(globalVar); // 'global' console.log(outerVar); // 'outer' console.log(innerVar); // 'inner' } innerFunction(); } outerFunction();
在这个例子中,innerFunction
可以访问全局变量 globalVar
,外层函数 outerVar
,以及自身作用域中的 innerVar
,这就是作用域链的工作方式。
91. 什么是事件循环(Event Loop)?
-
事件循环的定义:
事件循环 是 JavaScript 处理异步操作的机制。JavaScript 是单线程语言,它通过事件循环机制使得异步任务不会阻塞主线程,从而实现非阻塞的并发执行。 -
事件循环的工作原理:
- 同步任务 被直接放入主线程执行栈中,按顺序执行。
- 异步任务(如定时器、网络请求)会被放入 任务队列 中,当主线程执行栈为空时,事件循环会检查任务队列,依次执行任务队列中的任务。
-
微任务与宏任务:
- 宏任务 (macro task):如
setTimeout
、setInterval
、I/O 操作等。 - 微任务 (micro task):如
Promise.then()
、MutationObserver
等。 - 每次事件循环会先清空所有微任务队列,然后再执行一个宏任务。
- 宏任务 (macro task):如
-
事件循环示例:
console.log('Start'); setTimeout(() => { console.log('宏任务:setTimeout'); }, 0); Promise.resolve().then(() => { console.log('微任务:Promise.then'); }); console.log('End');
执行结果:
Start End 微任务:Promise.then 宏任务:setTimeout
解释:同步任务先执行,随后执行所有微任务,最后执行宏任务。
92. 什么是跨域?如何解决跨域问题?
-
跨域的定义:
跨域 是指浏览器执行XMLHttpRequest
或Fetch
请求时,因 同源策略 限制,不允许访问不同源的资源。同源指协议、域名、端口号必须一致,跨域即其中任一不一致。 -
跨域的常见解决方法:
-
JSONP (JSON with Padding):
- 利用
<script>
标签不受跨域限制的特点,通过动态插入<script>
标签来请求跨域资源。服务端返回一个包含回调函数的 JSON 数据。 - 缺点:只支持
GET
请求。
<script> function handleResponse(data) { console.log(data); } </script> <script src="https://example.com/data?callback=handleResponse"></script>
- 利用
-
CORS (Cross-Origin Resource Sharing):
- 服务端设置响应头
Access-Control-Allow-Origin
来允许跨域请求。 - 示例:
Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: GET, POST Access-Control-Allow-Headers: Content-Type
- 服务端设置响应头
-
代理服务器:
- 通过设置一个代理服务器,由同源的服务器发送请求到不同源的服务器,从而实现跨域。
-
服务器端转发:
- 在服务器上设置路由,服务器请求跨域资源并将结果返回给客户端,从而避免浏览器的跨域限制。
-
93. 防抖与节流
-
防抖 (Debounce):
防抖是一种函数优化技术,用于限制函数的执行频率。防抖的关键在于,如果在一定时间内函数再次被触发,计时器将重新计时。适用于输入框输入或窗口调整等频繁触发的事件。- 防抖实现:
function debounce(func, wait) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => { func.apply(this, args); }, wait); }; } window.addEventListener('resize', debounce(() => { console.log('窗口大小已调整'); }, 500));
- 防抖实现:
-
节流 (Throttle):
节流同样是一种函数优化技术,但与防抖不同,节流限制的是一段时间内函数只会执行一次。适用于页面滚动或鼠标移动等持续触发的事件。- 节流实现:
function throttle(func, limit) { let inThrottle; return function (...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => { inThrottle = false; }, limit); } }; } window.addEventListener('scroll', throttle(() => { console.log('页面正在滚动'); }, 1000));
- 节流实现:
94. Set 和 Map 的区别
-
Set:
-
Set 是一种无序且唯一的集合,可以存储任何类型的值。
-
常见方法:
add(value)
:向 Set 中添加值。has(value)
:检查值是否存在于 Set 中。delete(value)
:从 Set 中删除值。
-
Set 示例:
const set = new Set(); set.add(1); set.add(2); set.add(2); // 重复值不会添加 console.log(set.has(1)); // true set.delete(1); console.log(set); // Set { 2 }
-
-
Map:
-
Map 是一种键值对集合,允许任何类型的键。
-
常见方法:
set(key, value)
:向 Map 中添加键值对。get(key)
:根据键获取值。delete(key)
:删除键值对。
-
Map 示例:
const map = new Map(); map.set('name', 'Alice'); map.set('age', 25); console.log(map.get('name')); // 'Alice' map.delete('age'); console.log(map); // Map { 'name' => 'Alice' }
-