JS 篇
1. 解释 JavaScript 中的数据类型。
- 原始类型:
undefined
、null
、boolean
、number
、string
、symbol
、bigint
。 - 引用类型:对象(对象、数组、函数等)。
2. 什么是闭包(closure)?
- 闭包是指在一个函数内部定义的函数可以访问其外部函数的变量。
- 示例:javascript
function outerFunction() { let outerVariable = 'I am outside!' function innerFunction() { console.log(outerVariable) // 可以访问outerVariable } return innerFunction } const inner = outerFunction() inner() // 输出 'I am outside!'
3. 解释 JavaScript 的事件循环(event loop)。
- 事件循环是 JavaScript 的执行模型,它使得 JavaScript 可以进行非阻塞的异步操作。
- 执行栈(call stack)和消息队列(message queue)协同工作,事件循环不断检查执行栈是否为空,如果为空,则从消息队列中取出下一个任务并执行。
4. 什么是this
关键字?如何在不同的上下文中使用它?
this
引用当前执行上下文的对象。- 在对象方法中,
this
指向对象本身。 - 在全局函数中,
this
指向全局对象(浏览器中为window
)。 - 在构造函数中,
this
指向新创建的对象。 - 使用箭头函数时,
this
保持为定义时的上下文。
5. 解释 JavaScript 中的原型继承(prototypal inheritance)。
- JavaScript 使用原型链(prototype chain)实现继承。
- 每个对象都有一个
__proto__
属性指向其原型对象。 - 当访问对象的属性时,如果该对象没有该属性,则会在其原型链上查找。
6. 什么是箭头函数?它与普通函数有何不同?
- 箭头函数是 ES6 引入的简写函数语法。
- 与普通函数的不同:
- 语法更简洁。
- 没有自己的
this
,arguments
,super
和new.target
。 - 不能用作构造函数。
- 没有
prototype
属性。
7. 解释 JavaScript 中的立即调用函数表达式(IIFE)。
- IIFE 是一个立即执行的函数表达式,通常用于创建一个新的作用域,避免变量污染全局作用域。
- 示例:javascript
;(function () { console.log('This is an IIFE') })()
8. 什么是函数柯里化(currying)?
- 函数柯里化是将一个多参数函数转换成一系列单参数函数的技术。
- 示例:javascript
function curry(f) { return function (a) { return function (b) { return f(a, b) } } } const add = (a, b) => a + b const curriedAdd = curry(add) console.log(curriedAdd(1)(2)) // 输出3
9. 解释 JavaScript 中的作用域链(scope chain)。
- 作用域链是由当前执行上下文和其父级执行上下文的变量对象组成的链条。
- 当查找变量时,JavaScript 引擎首先在当前执行上下文中查找,如果没有找到,则沿着作用域链向上查找,直到全局上下文。
10. 如何克隆一个对象?
- 使用
Object.assign
或展开运算符:javascriptconst original = { a: 1, b: 2 } const clone1 = Object.assign({}, original) const clone2 = { ...original }
- 使用
JSON.parse
和JSON.stringify
(深拷贝):javascriptconst deepClone = JSON.parse(JSON.stringify(original))
11. 如何合并两个对象?
- 使用
Object.assign
或展开运算符:javascriptconst obj1 = { a: 1 } const obj2 = { b: 2 } const merged1 = Object.assign({}, obj1, obj2) const merged2 = { ...obj1, ...obj2 }
12. 解释数组的常用方法,如map
、filter
和reduce
。
map
:对数组的每个元素调用提供的函数,并返回新数组。javascriptconst arr = [1, 2, 3] const mappedArr = arr.map((x) => x * 2) // [2, 4, 6]
filter
:对数组的每个元素调用提供的函数,返回满足条件的元素的新数组。javascriptconst filteredArr = arr.filter((x) => x > 1) // [2, 3]
reduce
:对数组的每个元素调用提供的函数,将其累积成单个值并返回。javascriptconst sum = arr.reduce((acc, x) => acc + x, 0) // 6
13. 如何检测数组中是否包含某个值?
- 使用
includes
方法:javascriptconst arr = [1, 2, 3] console.log(arr.includes(2)) // true
14. 解释 JavaScript 中的回调函数(callback)。
- 回调函数是作为参数传递给另一个函数的函数,通常用于异步操作的处理。
- 示例:javascript
function doSomething(callback) { setTimeout(() => { console.log('Task done') callback() }, 1000) } doSomething(() => { console.log('Callback executed') })
15. 什么是 Promise?如何使用?
Promise 是用于处理异步操作的一种机制。
它代表一个异步操作的最终完成(或失败)及其结果值。
示例:
javascriptconst promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Success') }, 1000) }) promise .then((result) => { console.log(result) // 输出 'Success' }) .catch((error) => { console.error(error) })
16. 解释async
和await
。
async
关键字用于定义一个异步函数,函数返回一个 Promise。await
关键字用于等待一个 Promise 解决,它只能在async
函数内使用。- 示例:javascript
async function fetchData() { try { const response = await fetch('https://api.example.com/data') const data = await response.json() console.log(data) } catch (error) { console.error('Error:', error) } } fetchData()
17. 如何处理多个 Promise?
使用
Promise.all
或Promise.race
:javascriptconst promise1 = Promise.resolve(1) const promise2 = Promise.resolve(2) const promise3 = Promise.resolve(3) Promise.all([promise1, promise2, promise3]).then((values) => { console.log(values) // [1, 2, 3] }) Promise.race([promise1, promise2, promise3]).then((value) => { console.log(value) // 1 })
18. 如何使用 JavaScript 选择 DOM 元素?
- 使用
document.getElementById
、document.querySelector
、document.querySelectorAll
等方法:javascriptconst elementById = document.getElementById('myId') const elementByQuery = document.querySelector('.myClass') const elementsByQueryAll = document.querySelectorAll('.myClass')
19. 如何创建和插入 DOM 元素?
- 使用
document.createElement
和相关方法:javascriptconst newElement = document.createElement('div') newElement.textContent = 'Hello, World!' document.body.appendChild(newElement)
20. 如何使用 JavaScript 修改 DOM 元素的内容和属性?
- 修改内容和属性:javascript
const element = document.getElementById('myElement') element.textContent = 'New Content' element.setAttribute('data-attr', 'value')
21. 如何使用事件监听器(event listener)?
- 使用
addEventListener
方法:javascriptconst button = document.getElementById('myButton') button.addEventListener('click', () => { console.log('Button clicked') })
22. 如何进行错误处理?
- 使用
try...catch
语句:
try {
// 可能会抛出错误的代码
throw new Error('Something went wrong')
} catch (error) {
console.error('Error caught:', error)
} finally {
console.log('This will always execute')
}
23. 如何处理异步操作中的错误?
- 使用
.catch
方法处理 Promise 的错误:javascriptfetch('https://api.example.com/data') .then((response) => response.json()) .then((data) => { console.log(data) }) .catch((error) => { console.error('Error:', error) })
- 使用
try...catch
处理async/await
中的错误:javascriptasync function fetchData() { try { const response = await fetch('https://api.example.com/data') const data = await response.json() console.log(data) } catch (error) { console.error('Error:', error) } } fetchData()
24. 什么是Symbol
?它的用途是什么?
Symbol
是 ES6 引入的一种新的原始数据类型,表示独一无二的值。- 通常用于定义对象的唯一属性,防止属性名冲突。
- 示例:javascript
const uniqueKey = Symbol('key') const obj = { [uniqueKey]: 'value' } console.log(obj[uniqueKey]) // 输出 'value'
25. 解释WeakMap
和WeakSet
。
WeakMap
和WeakSet
是 ES6 引入的两个新的集合类型,用于存储弱引用对象。- 在
WeakMap
和WeakSet
中,引用的对象可以被垃圾回收,即使它们仍然存在于集合中。 - 示例:javascript
const weakMap = new WeakMap() const obj = {} weakMap.set(obj, 'value') console.log(weakMap.get(obj)) // 输出 'value'
26. 解释 ES6 中的解构赋值。
- 解构赋值允许从数组或对象中提取值,并将其赋值给变量。
- 示例:javascript
const [a, b] = [1, 2] const { x, y } = { x: 10, y: 20 }
27. 什么是Promise.all
和Promise.race
?
Promise.all
:接受一个 Promise 的数组,返回一个新的 Promise,只有当数组中所有 Promise 都解决时,新 Promise 才会解决。Promise.race
:接受一个 Promise 的数组,返回一个新的 Promise,一旦数组中任意一个 Promise 解决或拒绝,新的 Promise 就会解决或拒绝。示例:
javascriptconst promise1 = new Promise((resolve) => setTimeout(resolve, 100, 'one')) const promise2 = new Promise((resolve) => setTimeout(resolve, 200, 'two')) Promise.all([promise1, promise2]).then((values) => { console.log(values) // ["one", "two"] }) Promise.race([promise1, promise2]).then((value) => { console.log(value) // "one" })
28. 如何使用 ES6 模块?
使用
export
导出模块,使用import
导入模块。示例:
javascript// module.js export const foo = 'foo' export function bar() { return 'bar' } // main.js import { foo, bar } from './module.js' console.log(foo) // 输出 'foo' console.log(bar()) // 输出 'bar'
29. 如何在 Node.js 中使用模块?
使用
require
导入模块,使用module.exports
导出模块。示例:
javascript// module.js const foo = 'foo' const bar = () => 'bar' module.exports = { foo, bar } // main.js const { foo, bar } = require('./module.js') console.log(foo) // 输出 'foo' console.log(bar()) // 输出 'bar'
30. 解释event delegation
(事件委托)。
- 事件委托是将事件监听器添加到一个父元素,而不是每个子元素,通过事件冒泡机制来处理子元素的事件。
- 示例:javascript
const parent = document.getElementById('parent') parent.addEventListener('click', (event) => { if (event.target && event.target.matches('button')) { console.log('Button clicked', event.target) } })
31. 解释 JavaScript 中的生成器函数(generator function)。
生成器函数是 ES6 引入的一种函数,用于控制函数的执行。
生成器函数使用
function*
定义,执行时返回一个生成器对象,该对象可以通过next()
方法逐步执行函数代码。示例:
javascriptfunction* generatorFunction() { yield 'First output' yield 'Second output' return 'Done' } const generator = generatorFunction() console.log(generator.next().value) // 'First output' console.log(generator.next().value) // 'Second output' console.log(generator.next().value) // 'Done'
32. 解释 JavaScript 中的代理(Proxy)和反射(Reflect)。
Proxy:用于定义自定义行为的对象,可以拦截和修改对目标对象的基本操作(如属性查找、赋值、枚举、函数调用等)。
Reflect:提供一组与代理方法对应的静态方法,使得操作对象属性的语义更清晰。
示例:
javascriptconst target = { message: 'Hello, World!' } const handler = { get: function (target, property) { return property in target ? target[property] : `Property ${property} not found.` } } const proxy = new Proxy(target, handler) console.log(proxy.message) // 'Hello, World!' console.log(proxy.nonExistent) // 'Property nonExistent not found.' const success = Reflect.set(target, 'message', 'Hello, Proxy!') console.log(target.message) // 'Hello, Proxy!'
33. 解释 JavaScript 中的事件冒泡(event bubbling)和事件捕获(event capturing)。
事件冒泡:事件从最具体的元素开始(目标元素),逐级向上传播到最不具体的元素(通常是
document
)。事件捕获:事件从最不具体的元素开始,逐级向下传播到最具体的元素(目标元素)。
可以通过第三个参数
true
(捕获阶段)或false
(冒泡阶段)来控制事件监听器。示例:
javascriptdocument.getElementById('child').addEventListener('click', () => { console.log('Child clicked') }) document.getElementById('parent').addEventListener('click', () => { console.log('Parent clicked') })
34. 解释 JavaScript 中的深拷贝和浅拷贝。
浅拷贝:只复制对象的引用,修改副本会影响原对象。常见方法有
Object.assign
和展开运算符。深拷贝:复制对象的所有属性,修改副本不会影响原对象。常见方法有
JSON.parse(JSON.stringify(obj))
或递归复制。示例:
javascriptconst obj = { a: 1, b: { c: 2 } } const shallowCopy = { ...obj } shallowCopy.b.c = 3 console.log(obj.b.c) // 3 const deepCopy = JSON.parse(JSON.stringify(obj)) deepCopy.b.c = 4 console.log(obj.b.c) // 3
35. 解释 JavaScript 中的防抖(debounce)和节流(throttle)。
防抖:在事件被触发一定时间后执行,如果在这段时间内事件再次触发,则重新计时。用于减少频繁事件触发。
节流:在一定时间间隔内只执行一次事件处理函数,无论事件被触发多少次。用于控制事件触发频率。
示例:
javascriptfunction debounce(func, wait) { let timeout return function (...args) { clearTimeout(timeout) timeout = setTimeout(() => func.apply(this, args), wait) } } function throttle(func, limit) { let inThrottle return function (...args) { if (!inThrottle) { func.apply(this, args) inThrottle = true setTimeout(() => (inThrottle = false), limit) } } }
36. 解释 JavaScript 中的模块模式(module pattern)。
模块模式是一种设计模式,用于创建具有私有和公共属性和方法的模块。通过立即调用函数表达式(IIFE)实现。
示例:
javascriptconst Module = (function () { let privateVariable = 'I am private' function privateMethod() { console.log(privateVariable) } return { publicMethod: function () { privateMethod() } } })() Module.publicMethod() // 'I am private'
37. 如何优化 JavaScript 代码的性能?
使用事件委托来减少事件处理程序的数量。
避免不必要的 DOM 操作,使用文档片段(DocumentFragment)来批量更新 DOM。
使用
requestAnimationFrame
优化动画和重绘。使用 Web Workers 来进行繁重的计算任务。
示例:
javascript// 使用事件委托 document.getElementById('parent').addEventListener('click', (event) => { if (event.target.matches('.child')) { console.log('Child clicked') } }) // 使用requestAnimationFrame function animate() { requestAnimationFrame(animate) // 动画逻辑 } animate()
38. 解释 JavaScript 中的内存泄漏(memory leak)。
- 内存泄漏是指程序中动态分配的内存由于某种原因未被释放,导致内存被耗尽。
- 常见原因包括:未清理的全局变量、闭包中未被释放的引用、DOM 元素的循环引用等。
- 示例:javascript
function createLeak() { let leak = [] setInterval(() => { leak.push('leak') }, 1000) } createLeak() // 内存泄漏
39. 如何检测和防止内存泄漏?
- 使用开发者工具(如 Chrome DevTools)中的内存分析工具。
- 避免不必要的全局变量和闭包。
- 使用
WeakMap
和WeakSet
来存储临时对象。 - 示例:javascript
const weakMap = new WeakMap() let obj = {} weakMap.set(obj, 'value') obj = null // 对象被垃圾回收
40. 如何处理 JavaScript 中的浏览器兼容性问题?
使用
babel
将现代 JavaScript 代码转译为兼容旧浏览器的代码。使用
polyfill
为旧浏览器添加缺失的功能。使用 CSS 前缀和工具(如
autoprefixer
)处理 CSS 兼容性问题。示例:
javascript// Babel转译 // 安装babel-cli和babel-preset-env npm install --save-dev @babel/core @babel/cli @babel/preset-env // babel.config.js module.exports = { presets: ['@babel/preset-env'], }; // 使用命令行转译 npx babel src --out-dir dist
41. 什么是transpile
和polyfill
?
- Transpile:将源代码从一种高级语言转换为另一种高级语言。例如,将 ES6+代码转译为 ES5 代码。
- Polyfill:为旧环境添加现代功能的代码实现。例如,为不支持
Promise
的浏览器提供Promise
实现。 - 示例:javascript
// Polyfill示例 if (!window.Promise) { window.Promise = /* Promise 实现 */; }
42. 解释 JavaScript 中的懒加载(lazy loading)。
懒加载是一种设计模式,用于延迟加载资源(如图片、脚本)直到真正需要时才加载,改善页面加载速度和性能。
对图片懒加载可以使用
IntersectionObserver
API 或loading="lazy"
属性。示例:
javascript// 使用 IntersectionObserver const lazyImages = document.querySelectorAll('.lazy') const observer = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { if (entry.isIntersecting) { const img = entry.target img.src = img.dataset.src img.classList.remove('lazy') observer.unobserve(img) } }) }) lazyImages.forEach((img) => observer.observe(img))
43. 如何防范 JavaScript 中的 XSS(跨站脚本)攻击?
- 验证和清理用户输入,防止注入恶意代码。
- 使用
innerText
或textContent
而不是innerHTML
来插入用户生成的内容。 - 使用 HTTP 头
Content-Security-Policy
(CSP)来限制加载外部资源。 - 示例:javascript
// 通过编码用户输入来防止XSS function sanitizeInput(input) { const div = document.createElement('div') div.innerText = input return div.innerHTML } const userInput = '<script>alert("XSS")</script>' document.getElementById('output').innerHTML = sanitizeInput(userInput)
44. 解释 JavaScript 中的 CSRF(跨站请求伪造)攻击及其防范措施。
CSRF 攻击通过欺骗用户浏览器在用户不知情的情况下执行不当操作。
防范措施:
- 使用 CSRF 令牌(token)验证请求的合法性。
- 验证 HTTP 请求头中的
Origin
和Referer
字段。 - 使用 SameSite 属性配置 Cookie。
示例:
javascript// 后端生成和验证CSRF令牌示例 app.get('/form', (req, res) => { const token = generateCSRFToken() res.render('form', { csrfToken: token }) }) app.post('/submit', (req, res) => { if (req.body.csrfToken === req.session.csrfToken) { // 处理合法请求 } else { res.status(403).send('CSRF token mismatch') } })
45. 如何进行 JavaScript 代码的单元测试?
使用 JavaScript 测试框架如 Jest、Mocha、Chai 等进行单元测试。
示例:
javascript// 使用Jest进行单元测试 // 安装Jest:npm install --save-dev jest // package.json中配置测试脚本:"test": "jest" // sum.js function sum(a, b) { return a + b } module.exports = sum // sum.test.js const sum = require('./sum') test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3) }) // 运行测试:npm test
46. 解释 JavaScript 中的端到端(E2E)测试。
端到端测试模拟真实用户交互,测试整个应用的工作流程。
常用工具有 Cypress、Selenium、Puppeteer 等。
示例:
javascript// 使用Cypress进行E2E测试 // 安装Cypress:npm install --save-dev cypress // cypress/integration/spec.js describe('My First Test', () => { it('Visits the app root url', () => { cy.visit('http://localhost:3000') cy.contains('h1', 'Welcome') }) }) // 运行测试:npx cypress open
47. 解释 JavaScript 中的元编程(metaprogramming)。
元编程是编写操作其他代码的代码,主要包括使用
Proxy
、Reflect
、Symbol
等特性。Proxy
:用于创建拦截并自定义对象基本操作的对象。Reflect
:提供与代理方法对应的一组静态方法。Symbol
:用于创建独一无二的值,可用于对象属性名。示例:
javascriptconst handler = { get: function (target, prop, receiver) { console.log(`Property ${prop} has been accessed`) return Reflect.get(...arguments) } } const proxy = new Proxy({}, handler) proxy.test = 'Hello' console.log(proxy.test) // Property test has been accessed
48. 解释 JavaScript 中的装饰器(decorator)。
装饰器是 ES6 提案中的特性,用于修改类和类成员的行为。
常见于 TypeScript 和一些框架如 Angular 中。
示例:
typescript// TypeScript中的装饰器 function log( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const originalMethod = descriptor.value descriptor.value = function (...args: any[]) { console.log(`Called ${propertyKey} with args: ${args}`) return originalMethod.apply(this, args) } } class Example { @log sum(a: number, b: number) { return a + b } } const example = new Example() example.sum(1, 2) // Called sum with args: 1,2
49. 解释 JavaScript 中的多线程。
JavaScript 是单线程的,但可以使用 Web Workers 来实现并发任务。
Web Workers 允许在独立线程中运行脚本,不阻塞主线程。
示例:
javascript// worker.js self.onmessage = function (e) { const result = e.data[0] * e.data[1] self.postMessage(result) } // main.js const worker = new Worker('worker.js') worker.onmessage = function (e) { console.log('Result:', e.data) } worker.postMessage([10, 20]) // 发送数据到worker线程
50. 解释 JavaScript 中的模块打包工具。
模块打包工具用于将多个模块和依赖项打包成单个文件,以便在浏览器中使用。
常用工具包括 Webpack、Parcel、Rollup 等。
示例:
javascript// 使用Webpack进行模块打包 // 安装Webpack:npm install --save-dev webpack webpack-cli // webpack.config.js const path = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] } } // 运行Webpack:npx webpack
51. 解释 JavaScript 中的作用域(scope)。
作用域是指变量和函数的可访问范围。
JavaScript 有全局作用域和局部作用域(函数作用域和块作用域)。
示例:
javascriptvar globalVar = 'global' function example() { var localVar = 'local' console.log(globalVar) // 'global' console.log(localVar) // 'local' } example() console.log(localVar) // ReferenceError: localVar is not defined
52. 解释 JavaScript 中的闭包(closure)。
闭包是指在函数内部定义的函数可以访问其外部函数作用域中的变量。
闭包使得函数能够“记住”它的作用域链。
示例:
javascriptfunction outerFunction() { var outerVar = 'I am outer' function innerFunction() { console.log(outerVar) } return innerFunction } const inner = outerFunction() inner() // 'I am outer'
53. 解释 JavaScript 中的原型继承。
JavaScript 使用原型链来实现继承。每个对象都有一个原型(
__proto__
),对象可以通过原型链访问继承的属性和方法。示例:
javascriptfunction Person(name) { this.name = name } Person.prototype.greet = function () { console.log(`Hello, my name is ${this.name}`) } const alice = new Person('Alice') alice.greet() // 'Hello, my name is Alice'
54. 解释 JavaScript 中的事件循环(event loop)。
事件循环是 JavaScript 处理异步操作的机制。它通过任务队列和消息队列管理同步和异步代码的执行。
示例:
javascriptconsole.log('Start') setTimeout(() => { console.log('Timeout') }, 0) console.log('End') // 输出顺序:Start -> End -> Timeout
55. 解释 JavaScript 中的隐式类型转换。
- JavaScript 会在必要时自动将一种类型转换为另一种类型。这种转换有时会导致意想不到的结果。
- 示例:javascript
console.log('5' - 1) // 4 console.log('5' + 1) // '51' console.log(true + false) // 1
56. 解释 JavaScript 中的事件委托(event delegation)。
- 事件委托是将事件监听器添加到一个父元素,通过事件冒泡机制来管理多个子元素的事件。
- 示例:javascript
document.getElementById('parent').addEventListener('click', (event) => { if (event.target && event.target.matches('.child')) { console.log('Child clicked', event.target) } })
57. 解释 JavaScript 中的柯里化(currying)。
柯里化是将一个多参数函数转换成一系列单参数函数的技术。
示例:
javascriptfunction curry(func) { return function curried(...args) { if (args.length >= func.length) { return func.apply(this, args) } else { return function (...args2) { return curried.apply(this, args.concat(args2)) } } } } function add(a, b) { return a + b } const curriedAdd = curry(add) console.log(curriedAdd(1)(2)) // 3
58. 解释 JavaScript 中的记忆化(memoization)。
记忆化是一种优化技术,通过缓存函数调用的结果来提高性能。
示例:
javascriptfunction memoize(fn) { const cache = {} return function (...args) { const key = JSON.stringify(args) if (cache[key]) { return cache[key] } else { const result = fn(...args) cache[key] = result return result } } } const factorial = memoize((n) => { if (n <= 1) return 1 return n * factorial(n - 1) }) console.log(factorial(5)) // 120 console.log(factorial(5)) // 120 (cached result)
59. 解释如何在 JavaScript 中实现发布-订阅模式(pub-sub pattern)。
发布-订阅模式是一种设计模式,用于在对象之间进行松耦合的消息传递。
示例:
javascriptclass PubSub { constructor() { this.events = {} } subscribe(event, callback) { if (!this.events[event]) { this.events[event] = [] } this.events[event].push(callback) } unsubscribe(event, callback) { if (this.events[event]) { this.events[event] = this.events[event].filter((cb) => cb !== callback) } } publish(event, data) { if (this.events[event]) { this.events[event].forEach((callback) => callback(data)) } } } const pubsub = new PubSub() function handleEvent(data) { console.log('Event received:', data) } pubsub.subscribe('myEvent', handleEvent) pubsub.publish('myEvent', { key: 'value' }) pubsub.unsubscribe('myEvent', handleEvent)
60. 如何在 JavaScript 中实现链式调用(chaining)?
链式调用是一种设计模式,通过在方法中返回
this
对象来实现连续调用。示例:
javascriptclass Calculator { constructor(value = 0) { this.value = value } add(number) { this.value += number return this } subtract(number) { this.value -= number return this } multiply(number) { this.value *= number return this } divide(number) { if (number !== 0) { this.value /= number } return this } result() { return this.value } } const calc = new Calculator() const result = calc.add(10).subtract(2).multiply(4).divide(2).result() console.log(result) // 16
61. 如何避免 JavaScript 中的浮点数精度问题?
使用
Number.EPSILON
进行精度比较。使用整数运算代替浮点数运算。
示例:
javascriptfunction isEqual(a, b) { return Math.abs(a - b) < Number.EPSILON } console.log(isEqual(0.1 + 0.2, 0.3)) // true
62. 如何避免 JavaScript 中的内存泄漏?
避免未清理的全局变量和定时器。
使用
WeakMap
和WeakSet
来存储临时对象。示例:
javascriptlet map = new WeakMap() let obj = {} map.set(obj, 'value') obj = null // 对象被垃圾回收 // 避免未清理的定时器 let intervalId = setInterval(() => { // Some recurring task }, 1000) clearInterval(intervalId) // 清理定时器
63. 在实际项目中如何优化 JavaScript 的加载速度?
使用代码拆分和按需加载(code splitting & lazy loading)。
压缩和混淆 JavaScript 代码(使用工具如 Webpack 和 Terser)。
示例:
javascript// 使用Webpack进行代码拆分和按需加载 // webpack.config.js module.exports = { // ... optimization: { splitChunks: { chunks: 'all' } } } // 动态导入模块 import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => { console.log(_.join(['Hello', 'webpack'], ' ')) })
64. 如何在团队协作中维护 JavaScript 代码质量?
使用代码格式化工具如 Prettier。
使用静态代码分析工具如 ESLint。
进行代码评审(code review)和编写单元测试。
示例:
javascript// 安装并配置ESLint和Prettier // package.json { "scripts": { "lint": "eslint . --ext .js,.jsx", "format": "prettier --write ." }, "devDependencies": { "eslint": "^7.32.0", "prettier": "^2.3.2" } } // .eslintrc.js module.exports = { extends: ["eslint:recommended", "plugin:prettier/recommended"], env: { browser: true, node: true, es6: true, }, }; // .prettierrc { "singleQuote": true, "trailingComma": "es5" }
65. 解释 JavaScript 中的模板字符串(template literals)。
- 模板字符串使用反引号(
`
)定义,允许嵌入表达式和多行字符串。 - 示例:javascript
const name = 'Alice' const greeting = `Hello, ${name}!` const multiLine = `This is a multi-line string.` console.log(greeting) // 'Hello, Alice!' console.log(multiLine)
66. 解释 JavaScript 中的默认参数(default parameters)。
默认参数允许为函数参数设置默认值,当参数未提供时使用默认值。
示例:
javascriptfunction greet(name = 'Guest') { console.log(`Hello, ${name}!`) } greet('Alice') // 'Hello, Alice!' greet() // 'Hello, Guest!'
67. 解释 JavaScript 中的解构赋值(destructuring assignment)。
解构赋值允许从数组或对象中提取值,并赋给变量。
示例:
javascriptconst [a, b] = [1, 2] console.log(a, b) // 1, 2 const { name, age } = { name: 'Alice', age: 25 } console.log(name, age) // 'Alice', 25
68. 解释 JavaScript 中的扩展运算符(spread operator)。
扩展运算符
...
用于展开数组或对象。示例:
javascriptconst arr1 = [1, 2] const arr2 = [...arr1, 3, 4] console.log(arr2) // [1, 2, 3, 4] const obj1 = { a: 1, b: 2 } const obj2 = { ...obj1, c: 3 } console.log(obj2) // {a: 1, b: 2, c: 3}
69. 解释 JavaScript 中的生成器函数(generator function)。
生成器函数使用
function*
定义,可以生成一系列值,每次调用next()
方法返回一个值。示例:
javascriptfunction* 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
70. 解释 JavaScript 中的装饰器模式(decorator pattern)。
装饰器模式通过在不改变原始对象的情况下为其添加新功能来实现扩展。
示例:
javascriptfunction addLogging(fn) { return function (...args) { console.log(`Calling ${fn.name} with arguments: ${args}`) return fn(...args) } } function multiply(a, b) { return a * b } const loggedMultiply = addLogging(multiply) console.log(loggedMultiply(2, 3)) // 'Calling multiply with arguments: 2,3', 6
71. 解释 JavaScript 中的单例模式(singleton pattern)。
单例模式确保一个类只有一个实例,并提供全局访问点。
示例:
javascriptconst Singleton = (function () { let instance function createInstance() { const object = new Object('I am the instance') return object } return { getInstance: function () { if (!instance) { instance = createInstance() } return instance } } })() const instance1 = Singleton.getInstance() const instance2 = Singleton.getInstance() console.log(instance1 === instance2) // true
72. 解释如何在 JavaScript 中实现观察者模式(observer pattern)。
观察者模式定义了对象间的一对多依赖关系,当一个对象改变状态时,所有依赖者都会被通知并自动更新。
示例:
javascriptclass Subject { constructor() { this.observers = [] } addObserver(observer) { this.observers.push(observer) } removeObserver(observer) { this.observers = this.observers.filter((obs) => obs !== observer) } notifyObservers(message) { this.observers.forEach((observer) => observer.update(message)) } } class Observer { constructor(name) { this.name = name } update(message) { console.log(`${this.name} received message: ${message}`) } } const subject = new Subject() const observer1 = new Observer('Observer 1') const observer2 = new Observer('Observer 2') subject.addObserver(observer1) subject.addObserver(observer2) subject.notifyObservers('Hello Observers!') // Observer 1 and Observer 2 receive the message
73. 解释如何在 JavaScript 中实现策略模式(strategy pattern)。
策略模式定义一系列算法,并将每个算法封装在独立对象中,使得它们可以互换。
示例:
javascriptclass Strategy { execute(a, b) { throw new Error('This method should be overwritten!') } } class AddStrategy extends Strategy { execute(a, b) { return a + b } } class SubtractStrategy extends Strategy { execute(a, b) { return a - b } } class Context { constructor(strategy) { this.strategy = strategy } setStrategy(strategy) { this.strategy = strategy } executeStrategy(a, b) { return this.strategy.execute(a, b) } } const addStrategy = new AddStrategy() const subtractStrategy = new SubtractStrategy() const context = new Context(addStrategy) console.log(context.executeStrategy(5, 3)) // 8 context.setStrategy(subtractStrategy) console.log(context.executeStrategy(5, 3)) // 2
74. 解释 JavaScript 中的对象冻结(Object.freeze)和对象密封(Object.seal)。
Object.freeze
冻结对象,阻止对对象的修改(添加、删除、修改属性)。Object.seal
密封对象,阻止添加或删除属性,但允许修改现有属性。示例:
javascriptconst obj = { a: 1 } Object.freeze(obj) obj.a = 2 // 无效 console.log(obj.a) // 1 const obj2 = { b: 2 } Object.seal(obj2) obj2.b = 3 // 有效 delete obj2.b // 无效 console.log(obj2.b) // 3
75. 如何处理 JavaScript 中的异步错误?
使用
try...catch
块捕获异步函数中的错误。使用
Promise
的catch
方法处理错误。示例:
javascriptasync function fetchData() { try { let response = await fetch('https://api.example.com/data') let data = await response.json() console.log(data) } catch (error) { console.error('Error fetching data:', error) } } fetchData() // Promise处理 fetch('https://api.example.com/data') .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error('Error fetching data:', error))
76. 在实际项目中如何进行 JavaScript 代码分离(code splitting)?
使用模块化工具(如 Webpack)进行代码分离,按需加载模块,提高性能。
示例:
javascript// webpack.config.js module.exports = { entry: { main: './src/index.js', vendor: './src/vendor.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } } // 按需加载模块 document.getElementById('loadButton').addEventListener('click', () => { import('./module.js').then((module) => { module.loadFunction() }) })
77. 在团队项目中如何进行 JavaScript 代码版本控制?
使用版本控制系统(如 Git)管理代码,结合代码评审和持续集成工具。
示例:
bash# Git命令示例 git init git add . git commit -m "Initial commit" git remote add origin https://github.com/user/repo.git git push -u origin master # 创建和合并分支 git checkout -b feature-branch git add . git commit -m "Add new feature" git checkout master git merge feature-branch
78. 如何在 JavaScript 中进行 API 请求?
使用
fetch
API 或axios
库进行 API 请求,处理异步操作和错误。示例:
javascript// 使用 fetch fetch('https://api.example.com/data') .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error('Error:', error)) // 使用 axios axios .get('https://api.example.com/data') .then((response) => console.log(response.data)) .catch((error) => console.error('Error:', error))
79. 如何在 JavaScript 中进行单元测试?
使用测试框架(如 Jest、Mocha)和断言库(如 Chai)进行单元测试。
示例:
javascript// 安装 Jest npm install --save-dev jest // 示例函数 function sum(a, b) { return a + b; } // sum.test.js const sum = require('./sum'); test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); }); // 运行测试 npx jest
80. 如何在 JavaScript 中处理跨域请求(CORS)?
在服务器端设置 CORS 头部,允许跨域请求。
使用代理服务器解决开发环境中的跨域问题。
示例:
javascript// 在服务器端(如Express)设置CORS头部 const express = require('express') const app = express() app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*') res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE') res.header('Access-Control-Allow-Headers', 'Content-Type') next() }) app.get('/data', (req, res) => { res.json({ message: 'This is CORS-enabled' }) }) app.listen(3000, () => { console.log('Server running on port 3000') }) // 在前端使用代理服务器 // vue.config.js 或者 create-react-app 的 proxy 选项 module.exports = { devServer: { proxy: 'http://localhost:3000' } }
81. 解释 JavaScript 中的 Symbol 类型。
- Symbol 是一种原始数据类型,表示独一无二的值,通常用于定义对象的私有属性。
- 示例:javascript
const sym = Symbol('description') const obj = { [sym]: 'value' } console.log(obj[sym]) // 'value' console.log(sym.description) // 'description'
82. 解释 JavaScript 中的 BigInt 类型。
- BigInt 是一种原始数据类型,用于表示任意精度的整数。
- 示例:javascript
const bigInt = BigInt(1234567890123456789012345678901234567890n) const anotherBigInt = 1234567890123456789012345678901234567890n console.log(bigInt + anotherBigInt) // 2469135780246913578024691357802469135780n
83. 解释 JavaScript 中的空值合并运算符(nullish coalescing operator)。
- 空值合并运算符
??
在左操作数为null
或undefined
时返回右操作数,否则返回左操作数。 - 示例:javascript
const foo = null ?? 'default' const bar = undefined ?? 'default' const baz = 'value' ?? 'default' console.log(foo) // 'default' console.log(bar) // 'default' console.log(baz) // 'value'
84. 解释 JavaScript 中的可选链操作符(optional chaining operator)。
- 可选链操作符
?.
用于访问对象的属性和方法,如果前面的属性不存在不会抛出错误,而是返回undefined
。 - 示例:javascript
const obj = { a: { b: { c: 42 } } } console.log(obj?.a?.b?.c) // 42 console.log(obj?.a?.b?.d) // undefined
85. 解释 JavaScript 中的Proxy
对象。
Proxy
对象用于定义基本操作(如属性查找、赋值等)的自定义行为。示例:
javascriptconst target = { message1: 'hello', message2: 'everyone' } const handler = { get: function (target, prop, receiver) { if (prop === 'message1') { return 'world' } return Reflect.get(...arguments) } } const proxy = new Proxy(target, handler) console.log(proxy.message1) // 'world' console.log(proxy.message2) // 'everyone'
86. 解释 JavaScript 中的Reflect
对象。
Reflect
对象提供了一组用于操作对象的静态方法,这些方法与Proxy
对象的处理函数一一对应。- 示例:javascript
const obj = { x: 1, y: 2 } console.log(Reflect.get(obj, 'x')) // 1 Reflect.set(obj, 'x', 42) console.log(obj.x) // 42
87. 解释 JavaScript 中的WeakMap
和WeakSet
。
WeakMap
和WeakSet
与Map
和Set
类似,但其键(或值)只能是对象,并且这些对象是弱引用的,不会阻止垃圾回收。示例:
javascriptlet obj = {} const weakMap = new WeakMap() weakMap.set(obj, 'value') console.log(weakMap.get(obj)) // 'value' obj = null // obj 被垃圾回收 let obj2 = {} const weakSet = new WeakSet() weakSet.add(obj2) console.log(weakSet.has(obj2)) // true obj2 = null // obj2 被垃圾回收
88. 解释如何在 JavaScript 中实现模块化(modularization)。
使用 ES6 模块语法
import
和export
进行模块化,可以将代码分割成独立的文件和模块,提高代码可维护性。示例:
javascript// math.js export function add(a, b) { return a + b } export function subtract(a, b) { return a - b } // main.js import { add, subtract } from './math.js' console.log(add(2, 3)) // 5 console.log(subtract(5, 3)) // 2
89. 解释如何在 JavaScript 中实现惰性加载(lazy loading)。
- 惰性加载是指在需要时才加载模块或资源,可以使用
import()
动态导入模块。 - 示例:javascript
document.getElementById('loadButton').addEventListener('click', () => { import('./module.js').then((module) => { module.loadFunction() }) })
90. 解释如何在 JavaScript 中实现本地存储(local storage)。
- 使用
localStorage
对象在客户端存储数据,数据不会过期。 - 示例:javascript
localStorage.setItem('key', 'value') console.log(localStorage.getItem('key')) // 'value' localStorage.removeItem('key')
91. 解释如何在 JavaScript 中实现会话存储(session storage)。
- 使用
sessionStorage
对象在会话期间存储数据,数据在页面会话结束时被清除。 - 示例:javascript
sessionStorage.setItem('key', 'value') console.log(sessionStorage.getItem('key')) // 'value' sessionStorage.removeItem('key')
92. 解释 JavaScript 中的严格模式(strict mode)。
- 严格模式通过在代码顶部添加
"use strict";
启用,消除一些原有的错误,并使代码更安全。 - 示例:javascript
'use strict' x = 3.14 // ReferenceError: x is not defined
93. 解释 JavaScript 中的尾调用优化(tail call optimization)。
尾调用优化在严格模式下启用,允许在递归函数中重用栈帧,避免栈溢出。
示例:
javascript'use strict' function factorial(n, acc = 1) { if (n <= 1) return acc return factorial(n - 1, n * acc) } console.log(factorial(5)) // 120
94. 在实际项目中如何处理 JavaScript 中的错误和异常?
使用
try...catch
块捕获同步错误。使用
window.onerror
和window.addEventListener('unhandledrejection', handler)
捕获全局错误和未处理的 Promise 拒绝。示例:
javascripttry { // 可能会抛出错误的代码 } catch (error) { console.error('Error:', error) } window.onerror = function (message, source, lineno, colno, error) { console.error('Global error:', message, source, lineno, colno, error) } window.addEventListener('unhandledrejection', function (event) { console.error('Unhandled rejection:', event.reason) })
95. 在实际项目中如何处理 JavaScript 中的异步编程?
使用回调函数、Promise、
async/await
处理异步编程。示例:
javascript// 使用回调函数 function fetchData(callback) { setTimeout(() => { callback('data') }, 1000) } fetchData((data) => { console.log(data) }) // 使用Promise function fetchData() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('data') }, 1000) }) } fetchData().then((data) => { console.log(data) }) // 使用async/await async function fetchData() { return new Promise((resolve) => { setTimeout(() => { resolve('data') }, 1000) }) } async function main() { const data = await fetchData() console.log(data) } main()
96. 在团队项目中如何管理 JavaScript 依赖?
- 使用包管理工具(如 npm、Yarn)管理依赖,并通过
package.json
文件定义项目依赖。 - 示例:
# 初始化npm项目
npm init -y
# 安装依赖
npm install lodash
# 使用依赖
const _ = require('lodash');
console.log(_.camelCase('hello world'));
97. 在实际项目中如何进行 JavaScript 代码压缩和优化?
- 使用工具(如 Webpack、Parcel)进行代码压缩和优化,包括但不限于以下方法:
- 使用工具(如 UglifyJS、Terser)对 JavaScript 代码进行压缩和混淆,减小文件大小。
- 使用工具(如 Autoprefixer、PostCSS)对 CSS 进行自动添加前缀和优化。
- 使用工具(如 imagemin、svgo)对图片和 SVG 进行压缩和优化。
- 按需加载资源,减少页面加载时间和带宽占用。
- 使用 Webpack 或 Parcel 等打包工具进行模块打包和代码分割,减少不必要的请求。
- 优化网络请求,使用 CDN、缓存等技术提高资源加载速度和性能。
- 使用性能分析工具(如 Chrome DevTools、Lighthouse)分析页面性能,并根据结果进行优化调整。
- 优化渲染性能,减少重绘和重排,提高页面响应速度和用户体验。
98. 在团队项目中如何保持 JavaScript 代码的一致性和规范性?
- 使用代码风格检查工具(如 ESLint)进行代码风格检查,并配置统一的规则和规范。
- 使用代码格式化工具(如 Prettier)自动格式化代码,保持一致的代码风格。
- 编写文档和代码注释,说明代码设计和用法,提高代码可读性和可维护性。
- 进行代码评审,定期审查代码,发现和纠正不符合规范的代码。
- 培训团队成员,提高他们的代码质量意识和规范遵循能力。
- 使用版本控制系统(如 Git)进行代码管理,保留历史记录,并使用分支管理功能进行代码开发和合并,确保代码的一致性和可追溯性。
99. 在团队项目中如何处理 JavaScript 中的安全性问题?
- 防止 XSS 攻击:对用户输入进行过滤和转义,避免直接插入到 HTML 中。
- 防止 CSRF 攻击:使用 CSRF token 验证请求来源,防止恶意请求。
- 防止 SQL 注入:使用参数化查询和 ORM 框架,避免拼接 SQL 语句。
- 防止点击劫持:使用 X-Frame-Options 头部或 Content Security Policy(CSP)阻止网页被嵌入到 iframe 中。
- 防止数据泄露:对敏感数据进行加密存储,限制敏感操作的访问权限。
- 更新依赖和组件:及时更新依赖包和框架,修复已知的安全漏洞。
- 定期进行安全审计:对代码、配置和服务器进行安全审计,发现和修复潜在的安全问题。
100. 在团队项目中如何进行 JavaScript 性能优化?
- 减少 HTTP 请求:合并和压缩资源文件,减少页面加载时间。
- 使用 CDN 加速:将静态资源部署到 CDN,加速文件加载速度。
- 图片优化:使用适当大小和格式的图片,尽量减小图片文件大小。
- 懒加载和预加载:按需加载资源,提高页面加载速度。
- 优化渲染性能:减少 DOM 操作、重绘和重排,提高页面渲染速度。
- 使用缓存:利用浏览器缓存和服务器缓存,减少重复请求和数据传输。
- 代码优化:减少不必要的计算和操作,优化算法和数据结构,提高代码执行效率。
- 使用性能分析工具:使用工具(如 Chrome DevTools、Lighthouse)分析页面性能,发现和解决性能瓶颈。
- 定期性能监控:监控页面加载速度、资源加载时间和用户交互响应时间,及时发现并解决性能问题。