本文深入解析 JavaScript 十大核心面试题,涵盖 this 指向的六种场景、事件冒泡与捕获的触发顺序、面向对象编程特性、for...in 与 for...of 的区别。重点讲解数组去重的五种方法(Set、Map、递归等)及数组扁平化实现,并对比 iframe 优缺点、函数柯里化、垃圾回收机制及 window.onload 与 DOMContentLoaded 的执行差异,帮助开发者系统掌握前端基础。
1. this 指向
在 JavaScript 中,this 的指向遵循“谁调用指向谁”的基本规则。
例如:
function foo(){
console.log("this",this);
}
new foo();
2. 全局上下文中的 this 指向 window 对象。
例如:
function foo(){
console.log("this",this);
}
foo();
3. 在构造函数中,this 指向新创建的实例对象。
4. 使用 call、apply 或 bind 方法可以强制改变 this 的指向。
5. 箭头函数没有自己的 this,其始终指向定义时所在的父级上下文。
2. 事件模型:事件委托、代理?如何让事件先冒泡后捕获
事件委托,也称为事件代理,是利用事件冒泡机制,将子元素的事件监听器绑定到父元素上。如果子元素阻止了事件冒泡,则无法实现委托。
addEventListener 方法接收三个参数:事件名、事件处理函数以及一个布尔值,用于指定是捕获阶段还是冒泡阶段触发。
若需让事件先冒泡后捕获,需调整监听器的注册顺序。
具体做法是给同一元素绑定两个 addEventListener,其中一个第三个参数设为 false(冒泡),另一个设为 true(捕获)。将冒泡监听器注册在捕获监听器之前,即可实现先冒泡后捕获的顺序。
3. 对象和面向对象
对象是一种复合数据类型,可在其中保存多个不同数据类型的属性。
面向对象是一种编程思想,主张“万物皆对象”,与面向过程相对。其核心概念包括类的继承(子类继承父类的方法和属性)、封装(核心是低耦合高内聚)以及多态(重载和重写)。JavaScript 是一门面向对象的语言。
JavaScript 本身基于面向对象构建,例如内置类 Array、Object、Function、String 等。Promise 是 ES6 新增的内置类,可通过 new Promise 创建实例来管理异步编程。
日常开发中常用的 Vue、React、jQuery 等框架也是基于面向对象构建的类,开发者通常通过创建这些类的实例来进行操作。
4. for...in 和 for...of 的区别
1、建议在遍历对象属性时使用 for...in,在遍历数组时使用 for...of。
2、for...in 循环迭代的是键(key),而 for...of 循环迭代的是值(value)。
3、for...of 不能直接用于普通对象,需要通过 Object.keys() 获取键名数组后配合使用。
5. 查找数组重复项
判断元素首次出现的位置和最后出现的位置下标是否相同,同时检查新数组中是否已存在该元素,若均满足条件则将其添加至新数组。
ES6 Set
使用 ES6 中的 Set 结构是最简单的去重方法。
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,
undefined, null,null, NaN,NaN,'NaN', 0, 0, 'a', 'a',{},{}];
function arr_unique1(arr){
return [...new Set(arr)];
//或者
//return Array.from(new Set(arr));
}
arr_unique1(arr); // (13)[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
利用 Map 数据结构去重
function arr_unique2(arr) {
let map = new Map();
let array = new Array(); // 数组用于返回结果
for (let i = 0; i < arr.length; i++) {
if(map .has(arr[i])) { // 如果有该key值
map .set(arr[i], true);
} else {
map .set(arr[i], false); // 如果没有该key值
array .push(arr[i]);
}
}
return array ;
}
console.log(arr_unique2(arr)); //(13) [1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
利用递归方式去重
function arr_unique3(arr) {
var array= arr;
var len = array.length;
array.sort(function(a,b){ //排序后更加方便去重
return a - b;
})
function loop(index){
if(index >= 1){
if(array[index] === array[index-1]){
array.splice(index,1);
}
loop(index - 1); //递归loop,然后数组去重
}
}
loop(len-1);
return array;
}
console.log(arr_unique3(arr)); //(14) [1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
forEach 结合 indexOf 方法
filter 结合 indexOf 方法
forEach 结合 includes 方法
6. 数组扁平化
数组扁平化旨在将一个多维数组转换为一个一维数组。
实现基本方式如下:
1、对数组的每一项进行遍历。
2、判断该项是否是数组。
4、若为数组则返回步骤1,继续执行迭代过程。
4、如果是数组,则回到步骤 1,继续迭代处理。
7. iframe 的优缺点有哪些
优点:
① iframe 能够原封不动地展示嵌入的网页内容;
② 若多个网页引用同一 iframe,只需修改 iframe 内容,即可更新所有调用页面的内容,方便快捷;
③ 为统一网站风格,可将头部和版本等公共部分写成一个页面,通过 iframe 嵌套,提高代码可重用性;
④ 可通过 iframe 解决加载缓慢的第三方内容(如图标和广告)问题。
缺点:
① 会产生很多页面,导致管理困难;
② iframe 框架结构有时令人困惑,若框架数量多,可能出现滚动条,分散访问者注意力,降低用户体验;
③ 代码复杂,且搜索引擎爬虫难以处理 iframe 中的内容,导致不利于搜索引擎优化(SEO);
④ 设备兼容性较差;
⑤ iframe 框架页面会增加服务器的 HTTP 请求,对于大型网站而言不可取。
8. 函数柯里化(卡瑞化、加里化)
函数柯里化是将接受多个参数的函数变换为接受单一参数(最初函数的第一个参数)的函数,并返回接受余下参数且返回结果的新函数的技术。
即只传递给函数某一部分参数来调用,返回一个新函数去处理剩下的参数(利用闭包)。
9. 垃圾回收机制
浏览器的 JavaScript 具有自动垃圾回收机制(GC: Garbage Collection),执行环境负责管理代码执行过程中使用的内存。其原理是:垃圾收集器定期(周期性)找出不再继续使用的变量,然后释放其内存。由于该过程开销较大且 GC 时会停止响应其他操作,因此垃圾回收器会按照固定的时间间隔周期性执行。
10. window 的 onload 事件和 DOMContentLoaded
window.onload:
当一个资源及其依赖资源已完成加载时,将触发 onload 事件。
document.onDOMContentLoaded:
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,无需等待样式表、图像和子框架的完成加载。
区别:
① onload 事件是 DOM 事件,DOMContentLoaded 是 HTML5 事件。
② onload 事件会被样式表、图像和子框架阻塞,而 DOMContentLoaded 不会。
③ 当加载的脚本内容并不包含立即执行 DOM 操作时,使用 DOMContentLoaded 事件是更好的选择,其执行时间会比 onload 更早。
总结
到此这篇关于 JavaScript 必看的 10 道面试题的文章就介绍到这了,更多相关 JavaScript 面试题内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!