面试题答案
一键面试函数调用方式优化
- 使用Function.prototype.bind:在一些老版本浏览器中,
this
的指向可能存在问题。bind
方法可以创建一个新函数,将原函数的this
绑定到指定对象,并且可以预设部分参数。function greet() { console.log('Hello, ', this.name); } const person = { name: 'John' }; const boundGreet = greet.bind(person); boundGreet();
- 避免不必要的函数包装:减少在循环或频繁调用的代码中创建匿名函数,因为每次创建都会产生额外开销。
// 不好的示例 for (let i = 0; i < 1000; i++) { setTimeout(() => { console.log(i); }, 0); } // 好的示例 function logNumber(num) { console.log(num); } for (let i = 0; i < 1000; i++) { setTimeout(logNumber.bind(null, i), 0); }
事件绑定优化
- 事件委托:将事件绑定到父元素,通过事件冒泡机制处理子元素的事件。这样可以减少事件处理器的数量,提高性能。
<ul id="parent"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul>
const parent = document.getElementById('parent'); parent.addEventListener('click', function (e) { if (e.target.tagName === 'LI') { console.log('Clicked on ', e.target.textContent); } });
- 减少事件绑定和解绑的频率:避免在频繁触发的代码块中进行事件的绑定和解绑操作。例如,在
scroll
或resize
事件中。可以使用防抖(Debounce)或节流(Throttle)技术。- 防抖:在事件触发一定时间后才执行函数,如果在这段时间内再次触发,则重新计时。
function debounce(func, delay) { let timer; return function () { const context = this; const args = arguments; clearTimeout(timer); timer = setTimeout(() => { func.apply(context, args); }, delay); }; } window.addEventListener('resize', debounce(() => { console.log('Window resized'); }, 300));
- 节流:在一定时间内,无论事件触发多少次,都只执行一次函数。
function throttle(func, limit) { let inThrottle; return function () { const context = this; const args = arguments; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } window.addEventListener('scroll', throttle(() => { console.log('Window scrolled'); }, 300));
内存管理优化
- 解除事件绑定:在不需要事件处理器时,及时解除绑定,防止内存泄漏。
function handleClick() { console.log('Clicked'); } const button = document.getElementById('button'); button.addEventListener('click', handleClick); // 之后如果不再需要该事件处理器 button.removeEventListener('click', handleClick);
- 避免循环引用:如果对象之间存在循环引用,可能导致垃圾回收机制无法回收内存。
function createCircularReference() { let obj1 = {}; let obj2 = {}; obj1.ref = obj2; obj2.ref = obj1; return { obj1, obj2 }; } // 尽量避免上述情况,若无法避免,在不再需要这些对象时,手动解除引用 let { obj1, obj2 } = createCircularReference(); obj1.ref = null; obj2.ref = null; obj1 = null; obj2 = null;
- 合理使用闭包:闭包可能会导致内存占用增加,因为闭包中的变量不会被垃圾回收。在闭包使用完毕后,及时释放相关引用。
function outer() { let largeData = new Array(1000000).fill(1); return function inner() { return largeData[0]; }; } let innerFunc = outer(); // 当不再需要innerFunc时 innerFunc = null;