回答: 闭包是指在一个函数内部定义的函数,能够访问其外部函数的作用域,即使外部函数已经执行完毕。
示例:
function outerFunction() {
let outerVariable = 'I am outside!';
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const closure = outerFunction();
closure(); // 输出: I am outside!
解析:
outerFunction
定义了一个内部函数 innerFunction
,并返回它。outerFunction
执行完毕,innerFunction
仍然可以访问 outerFunction
的变量 outerVariable
,这就是闭包。回答: 事件委托是一种利用事件冒泡机制,将事件处理器添加到父元素而不是每个子元素的方法。
示例:
<ul id="parent">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<script>
document.getElementById('parent').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
console.log(event.target.textContent);
}
});
</script>
解析:
#parent
,利用事件冒泡机制,子元素 <li>
的点击事件会冒泡到父元素进行处理。var
、let
和 const
之间的区别。回答:
var
:
undefined
。let
:
const
:
示例:
if (true) {
var x = 1;
let y = 2;
const z = 3;
}
console.log(x); // 输出: 1
console.log(y); // 报错: ReferenceError: y is not defined
console.log(z); // 报错: ReferenceError: z is not defined
回答: 可以使用 JSON 方法或递归函数进行深拷贝。
JSON 方法:
const original = { a: 1, b: { c: 2 } };
const copy = JSON.parse(JSON.stringify(original));
console.log(copy); // 输出: { a: 1, b: { c: 2 } }
递归函数:
function deepCopy(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
return copy;
}
const original = { a: 1, b: { c: 2 } };
const copy = deepCopy(original);
console.log(copy); // 输出: { a: 1, b: { c: 2 } }
回答: 箭头函数是 ES6 引入的简洁语法,允许以更简短的方式编写函数表达式。
示例:
const add = (a, b) => a + b;
console.log(add(2, 3)); // 输出: 5
不同之处:
function
关键字和 return
语句(单行表达式)。this
绑定,this
由外层作用域决定。new
关键字)。arguments
对象。回答: JavaScript 中的异步编程可以通过回调函数、Promise 和 async/await 来处理。
回调函数:
function fetchData(callback) {
setTimeout(() => {
callback('Data');
}, 1000);
}
fetchData((data) => {
console.log(data); // 输出: Data
});
Promise:
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Data');
}, 1000);
});
}
fetchData().then((data) => {
console.log(data); // 输出: Data
});
async/await:
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Data');
}, 1000);
});
}
async function fetchAsyncData() {
const data = await fetchData();
console.log(data); // 输出: Data
}
fetchAsyncData();
解析:
调用处理异步操作结果,避免回调地狱。
回答: JavaScript 是单线程的,但通过异步操作(如回调、Promise 和 async/await)和事件驱动机制,可以高效处理多个任务。事件驱动编程通过事件循环处理异步事件。
示例:
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
console.log('End');
输出:
Start
End
Timeout
解析:
setTimeout
是一个异步操作,回调函数被推迟到事件循环的下一轮执行。回答:
浅拷贝:复制对象的引用,而不是实际对象。改变拷贝后的对象也会影响原对象。
const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);
shallowCopy.b.c = 3;
console.log(original.b.c); // 输出: 3
深拷贝:创建对象的完整副本,修改拷贝不会影响原对象。
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.b.c = 4;
console.log(original.b.c); // 输出: 2
解析:
Object.assign
或扩展运算符(...
)实现浅拷贝,用 JSON 方法或递归函数实现深拷贝。null
和 undefined
的区别。回答:
undefined
:表示变量声明了但未赋值。
let a;
console.log(a); // 输出: undefined
null
:表示变量明确为空值。
let b = null;
console.log(b); // 输出: null
解析:
undefined
是 JavaScript 初始化变量的默认值。null
是开发者用来表示空值的显式赋值。回答:
防抖:在指定时间内不执行函数,直到停止触发一定时间后才执行。
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const debouncedFunction = debounce(() => {
console.log('Debounced!');
}, 2000);
window.addEventListener('resize', debouncedFunction);
节流:在指定时间内只执行一次函数。
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
const throttledFunction = throttle(() => {
console.log('Throttled!');
}, 2000);
window.addEventListener('resize', throttledFunction);
解析:
setTimeout
和 setInterval
的区别。回答:
setTimeout
:在指定时间后执行一次回调函数。
setTimeout(() => {
console.log('Executed after 1 second');
}, 1000);
setInterval
:每隔指定时间重复执行回调函数。
setInterval(() => {
console.log('Executed every 1 second');
}, 1000);
解析:
setTimeout
用于延迟执行代码。setInterval
用于周期性执行代码,直到被清除。try...catch
语句的用法及其作用。回答:
try...catch
语句用于处理代码中的异常,防止程序崩溃,并提供错误处理逻辑。
示例:
try {
let result = riskyOperation();
console.log(result);
} catch (error) {
console.error('An error occurred:', error.message);
}
解析:
try
块包含可能会引发异常的代码。catch
块包含处理异常的代码,当 try
块中发生异常时,程序控制流会转到 catch
块。for-await-of
循环。回答:
异步迭代器用于遍历异步可迭代对象,如异步生成器。for-await-of
循环用于异步遍历可迭代对象。
示例:
async function* asyncGenerator() {
let i = 0;
while (i < 3) {
yield new Promise(resolve => setTimeout(() => resolve(i++), 1000));
}
}
(async () => {
for await (const num of asyncGenerator()) {
console.log(num); // 输出: 0, 1, 2(每秒输出一个)
}
})();
解析:
async function*
定义异步生成器函数,yield
返回异步值。for-await-of
循环用于等待并异步遍历可迭代对象的值。Symbol
类型及其用途。回答:
Symbol
是 ES6 引入的一种原始数据类型,表示独一无二的标识符。它可以用于对象属性的唯一标识,避免属性名冲突。
示例:
const sym1 = Symbol('description');
const sym2 = Symbol('description');
console.log(sym1 === sym2); // 输出: false
const obj = {
[sym1]: 'value1',
[sym2]: 'value2'
};
console.log(obj[sym1]); // 输出: value1
console.log(obj[sym2]); // 输出: value2
解析:
Symbol
创建唯一的标识符,即使描述相同,也不会相等。Symbol
可以作为对象属性名,避免属性名冲突。Map
和 Set
。回答:
Map
:用于存储键值对,允许使用任何类型的值作为键。
const map = new Map();
map.set('key1', 'value1');
map.set('key2', 'value2');
console.log(map.get('key1')); // 输出: value1
console.log(map.size); // 输出: 2
Set
:用于存储唯一值,不允许重复的值。
const set = new Set();
set.add(1);
set.add(2);
set.add(1); // 重复的值不会被添加
console.log(set.has(1)); // 输出: true
console.log(set.size); // 输出: 2
解析:
Map
提供了键值对的数据结构,键和值可以是任意类型。Set
提供了一个包含唯一值的集合,自动去重。回答: JavaScript 模块化是将代码分割成独立的、可重用的模块,每个模块只负责一个功能。ES6 引入了原生的模块化支持。
示例:
导出模块(export.js):
export const name = 'Alice';
export function greet() {
console.log('Hello, ' + name);
}
导入模块(import.js):
import { name, greet } from './export.js';
console.log(name); // 输出: Alice
greet(); // 输出: Hello, Alice
解析:
export
关键字用于导出模块成员。import
关键字用于导入模块成员。async
和 await
。回答:
async
和 await
是 ES8 引入的用于处理异步操作的语法糖,使代码看起来像同步代码。
示例:
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Data');
}, 1000);
});
}
async function fetchAsyncData() {
const data = await fetchData();
console.log(data); // 输出: Data
}
fetchAsyncData();
解析:
async
关键字用于定义一个返回 Promise
的异步函数。await
关键字用于等待异步操作完成,并返回其结果。回答: 事件捕获和事件冒泡是事件传播的两个阶段。
示例:
<div id="parent">
<button id="child">Click me</button>
</div>
<script>
document.getElementById('parent').addEventListener('click', () => {
console.log('Parent clicked');
}, true); // 捕获阶段
document.getElementById('child').addEventListener('click', () => {
console.log('Child clicked');
});
</script>
输出顺序(捕获阶段优先):
Parent clicked
Child clicked
解析:
Proxy
对象及其用途。回答:
Proxy
对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。
示例:
const target = {
message1: "hello",
message2: "everyone"
};
const handler = {
get: function(target, prop, receiver) {
if (prop === "message2") {
return "world";
}
return Reflect.get(...arguments);
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.message1); // 输出: hello
console.log(proxy.message2); // 输出: world
解析:
Proxy
接受两个参数:目标对象和处理器对象。get
、set
等。Generator
函数。回答:
Generator
函数是 ES6 引入的一种函数,允许函数在执行过程中暂停和恢复,提供生成器对象。
示例:
function* generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
console.log(gen.next().value); // 输出: 1
console.log(gen.next().value); // 输出: 2
console.log(gen.next().value); // 输出: 3
解析:
function*
定义生成器函数,yield
关键字用于暂停函数执行并返回值。next
方法恢复执行并返回下一个 yield
的值。