2022 Web 前端面试题及答案 之 javaScript 篇
sinye56 2024-10-23 14:28 7 浏览 0 评论
再见 2021 ,你好 2022!前端妹子祝大家元旦快乐!
2022 第一天,给大家分享一篇面试相关文章,希望大家在 2022 年,摸鱼时间越来越多,薪资越涨越快!
1、事件循环机制
阿里面试题1:
<script type="text/javascript">
var p =new Promise(resolve=>{
console.log(4)
resolve(5)
})
function f1(){
console.log(1)
}
function f2(){
setTimeout(()=>{
console.log(2)
},0)
f1()
console.log(3)
p.then(res=>{
console.log(res)
})
}
f2()
</script>
// 运行结果 4 1 3 5 2
// 如果已经了解事件运行机制,就可以跳过该问题了
事件循环机制,event-loop 。包含三部分:调用栈、消息队列、微任务队列。
事件循环开始的时候,会从全局一行一行的执行代码,遇到函数调用的时候,就会压入调用栈中,当函数执行完成之后,弹出调用栈。
// 如:代码会一行一行执行,函数全部调用完成之后清空调用栈
function f1(){
console.log(1)
}
function f2(){
f1()
console.log(2)
}
f2()
// 执行结果 1 2
如果遇到 fetch、setInterval、setTimeout 异步操作时,函数调用压入调用栈时,异步执行内容会被加入消息队列中,消息队列中的内容会等到调用栈清空之后才会执行。
// 如:
function f1(){
console.log(1)
}
function f2(){
setTimeout(()=>{
console.log(2)
},0)
f1()
console.log(3)
}
f2()
// 执行结果 :1 3 2
遇到 promise、async、await 异步操作时,执行内容会被加入微任务队列中,会在调用栈清空之后立即执行。
调用栈加入的微任务队列会立即执行。
如
let p =new Promise(resolve=>{
console.log('立即执行')
resolve(1) //在 then 调用中执行
})
微任务队列中内容优先执行,所以比消息队列中的内容执行得早。
了解这些知识后,再试一下最前面的那道面试题,应该就没什么问题了。
2、你对作用域的认识有多少?
阿里面试题2:
<script type="text/javascript">
function fn(a,c){
console.log(a)
var a = 12
console.log(a)
console.log(c)
function a(){ }
if(false){
var d = 34
}
console.log(d)
console.log(b)
var b = function(){}
console.log(b)
function c(){}
console.log(c)
}
fn(1,2)
</script>
// 运行结果:
/*
function a(){}
12
function c(){}
undefined
undefined
function (){}
function c(){}
*/
作用域通俗地讲,就是指一个变量的作用范围。下面分别介绍下全局作用域和函数作用域的概念。
全局作用域
- 页面打开时被创建,页面关闭时被销毁。
- 编写在 script 标签下的变量和函数,作用域为全局,页面的任意位置都可以访问
- 有全局对象 window ,代表浏览器窗口,全局作用下的变量和函数作为 window 的属性和方法
函数作用域(局部)
- 函数是被调用时创建的,执行完毕之后销毁。
- 函数每调用一次,变量和函数就会重新创建一次,它们之间是相互独立的
- 在函数作用域内可以访问到全局变量或函数,但是在函数外无法访问函数作用域内的变量
- 函数作用域内访问变量,会在自身作用域内寻找,若没有则会向上一级作用域内查找,一直到全局作用域。
讲这些概念看完,发现还不会做上边的面试题,接下来就学习学习作用域的预编译,看看函数执行的时候都干了些啥?
函数在被调用的时候会先进行预编译:
全局作用域预编译:
- 创建上下文 GO 对象。
- 找变量声明,将变量名作为 GO 对象的属性名,值为 undefined
- 找函数式声明,将值赋予函数体
函数作用域预编译:
- 创建上下文 AO 对象
- 将形参和实参作为 AO 对象的属性,赋值为 undefined
- 实参和形参相统一
- 在函数体内找函数声明,将值赋予函数体。
了解预编译过程之后,我们将上面的面试题进行解析,分析下运行结果是怎么来的?
fn 函数调用的时候,先进行预编译,
第一阶段:生成一个 AO 对象
第二阶段:找到形参和实参,作为 AO 对象的属性名,值为 udefined 。
AO{
a : undefined,
b : undefined,
c : undefined,
d : undefined
}
第三阶段:实参和形参相统一,之后,AO对象改变为:
AO{
a : 1,
b : undefined,
c : 2,
d : undefined
}
第四阶段:找到函数声明,将值赋给变量,AO改变为:
AO{
a : function a(){ } ,
b : undefined,
c : function c(){ },
d : undefined
}
这下结合函数的预编译过程以及函数作用域概念,再尝试一下面试题,简单了吗?
3、为什么会有闭包?它解决了什么问题?
实例3:
var liArr = document.getElementsByTagName('li')
for(var i=0;i<liArr.length;i++){
liArr[i].onclick = function(){
console.log(liArr[i])
}
}
这是一个非常常见的实际应用,我们是想要点击元素然后操作对应的元素,但是点击之后发现打印出来的是 undefined 。我们应该能想到 i 变成了 liArr.length ,所以找不到对应元素,这个问题该如何解决呢?
说闭包时,必须介绍作用域。
上面介绍全局作用域和函数作用域,js内部变量的访问是由内向外的,内部可以访问到外部的变量,但是外部无法访问函数内的变量,如果我们在外部访问函数内的变量就需要使用闭包。
闭包就是函数嵌套函数,通过函数内的函数访问变量的规则,实现外部访问函数内的变量。
闭包的特点:
- 函数嵌套函数。
- 函数内部可以引用函数外部的参数和变量。
- 参数和变量不会被垃圾回收机制回收。
那么上述实例该如何使用闭包解决该问题呢?
实例3:闭包解决问题
var liArr = document.getElementsByTagName('li')
for(var i=0;i<liArr.length;i++){
(function(i){
liArr[i].onclick = function(){
console.log('点击元素',liArr[i])
}
})(i)
}
闭包优点:
- 保护变量安全,实现封装,防止变量声明冲突和全局污染。
- 在内存当中维持一个变量,可以做缓存。
- 匿名函数自执行函数可以减少内存消耗。
防抖和节流就是闭包的经典应用。
4、防抖和节流,你了解多少?
在实际应用中,常见的就是窗口的 resize、输入框搜索内容、scroll 等操作,如果这些操作触发频率太高,就会加重浏览器的负担,同时用户体验也较差。该如何优化该操作呢?
防抖函数是什么呢?
当持续触发事件,一定时间内没有再触发事件,事件处理函数才会执行一次,如果在设定的时间到来之前又触发了事件,就会重新计时。
实例4:我们想要制作一个输入框搜索,计划输入完成后两秒再执行,打印出输入的值。
function debounce(val){
var timer
clearTimeout(timer)
timer = setTimeout(function(){
console.log(val)
},2000)
}
var input = document.getElementById('input')
input.addEventListener('keyup',function(e){
debounce(e.target.value)
})
实际运行结果:我们发现输入之后,延时两秒之后打印出结果。
并非我们想要的结果,这是什么原因呢?
因为函数每次重新调用的时候 timer 会重新创建,调用完成之后就会被销毁,所以每次重新调用函数的时候,clearTimeout 内的 timer 都是 undefined 。所以我们需要把 timer 始终保持在内存当中,所以就需要使用闭包。
使用闭包修改上述实例4:
function debounce(delay){
var timer
return function(val){
clearTimeout(timer)
timer = setTimeout(function(){
console.log(val)
},delay)
}
}
var debounceFun = debounce(2000)
var input = document.getElementById('input')
input.addEventListener('keyup',function(e){
debounceFun(e.target.value)
})
防抖函数常见的实际应用:使用 echart 的时候,浏览器 resize 时,需要重新绘制图表大小,还有典型的输入框搜索应用。
节流函数是什么?
当持续触发事件的时候,保证一段时间内只调用一次事件处理函数,一段时间内,只允许做一件事情。
实例5:滚动条实现一段时间内执行一次处理,执行回调。
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log('执行回调');
}
window.addEventListener('scroll', throttle(handle, 1000));
防抖和节流主要是用来限制触发频率较高的事件,再不影响效果的前提条件下,降低事件触发频率,减小浏览器或服务器的压力,提升用户体验效果。
5、数组去重有几种方法?
这是一个非常常见的面试题,你知道几种方式呢?
var arr = [1,2,3,4,5,1,2,3,4]
function unique(arr){
//添加去重的方法的内容
}
unique(arr)
方法1: Set 方法
return Array.from(new Set(arr))
// 或
return [...new Set(arr)]
new Set 返回的数据不是数组,所以使用 Aray.from 方法将类数组转为真正的数组,或把 ...new Set(arr) 放入数组中。
方法2:使用两次循环
for(var i=0,len=arr.length;i<len;i++){
for(var j=i+1,len=arr.length;j<len;j++){
if( arr[i]===arr[j] ){
arr.splice(i,1)
j--;
len--
}
}
}
return arr
方法3:indexOf 实现
arr.indexOf(item) 返回 item 元素在 arr 数组中第一次出现所在位置的下标。
let arr1 = []
for(var i=0;i<arr.length;i++){
if( arr1.indexOf(arr[i]) === -1 ){
arr1.push(arr[i])
}
}
return arr1
方法4:includes 实现
let arr1 = []
for(var i=0;i<arr.length;i++){
if( !arr1.includes(arr[i]) ){
arr1.push(arr[i])
}
}
return arr1
方法5:filter 实现
array.indexOf(item,start) start 表示开始检索的位置。
return arr.filter(( item, index )=>{
return arr.indexOf( item, 0 ) == index
})
相关推荐
- 程序员:JDK的安装与配置(完整版)_jdk的安装方法
-
对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...
- Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置
-
一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...
- 麒麟系统安装JAVA JDK教程_麒麟系统配置jdk
-
检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...
- 学习笔记-Linux JDK - 安装&配置
-
前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...
- Linux新手入门系列:Linux下jdk安装配置
-
本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...
- 测试员必备:Linux下安装JDK 1.8你必须知道的那些事
-
1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...
- Linux 下安装JDK17_linux 安装jdk1.8 yum
-
一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...
- 在Ubuntu系统中安装JDK 17并配置环境变量教程
-
在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...
- 如何在 Linux 上安装 Java_linux安装java的步骤
-
在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...
- Windows和Linux环境下的JDK安装教程
-
JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...
- linux安装jdk_linux安装jdk软连接
-
JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...
- linux安装jdk,全局配置,不同用户不同jdk
-
jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...
- 运维大神教你在linux下安装jdk8_linux安装jdk1.7
-
1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...
- window和linux安装JDK1.8_linux 安装jdk1.8.tar
-
Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...
- 最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?
-
默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle忘记用户名密码 (59)
- oracle11gr2安装教程 (55)
- mybatis调用oracle存储过程 (67)
- oracle spool的用法 (57)
- oracle asm 磁盘管理 (67)
- 前端 设计模式 (64)
- 前端面试vue (56)
- linux格式化 (55)
- linux图形界面 (62)
- linux文件压缩 (75)
- Linux设置权限 (53)
- linux服务器配置 (62)
- mysql安装linux (71)
- linux启动命令 (59)
- 查看linux磁盘 (72)
- linux用户组 (74)
- linux多线程 (70)
- linux设备驱动 (53)
- linux自启动 (59)
- linux网络命令 (55)
- linux传文件 (60)
- linux打包文件 (58)
- linux查看数据库 (61)
- linux获取ip (64)
- linux进程通信 (63)