免费制作app生成器网站,seo优化外包顾问,推广软文平台,单品商城网站源码前端面试基础知识题
1. Promise中#xff0c;resolve后面的语句是否还会执行#xff1f;
会被执行。如果不需要执行#xff0c;需要在 resolve 语句前加上 return。
2. 什么是内存泄漏#xff1f;什么原因会导致呢#xff1f;
内存泄露的解释#xff1a;程序中己动态…前端面试基础知识题
1. Promise中resolve后面的语句是否还会执行
会被执行。如果不需要执行需要在 resolve 语句前加上 return。
2. 什么是内存泄漏什么原因会导致呢
内存泄露的解释程序中己动态分配的堆内存由于某种原因未释放或无法释放。
根据JS的垃圾回收机制当内存中引用的次数为0的时候内存才会被回收全局执行上下文中的对象被标记为不再使用才会被释放
内存泄露的几种场景
全局变量过多。通常是变量未被定义或者胡乱引用了全局变量
// main.js
// 场景1
function a(){b10;
}
a();
b;// 场景2
setTimeout((){console.log(b)
},1000)闭包。 未手动解决必包遗留的内存引用。定义了闭包就要消除闭包带来的副作用
function closuer (){const b 0;return (c) b c
}const render closuer();render();
render null; // 手动设置为nullGC会自己去清除事件监听未被移除
function addEvent (){const node document.getElementById(warp);node.addEventListener(touchmove,(){console.log(In Move);})
}const onTouchEnd (){const node document.getElementById(warp);node.
}useEffect(()(){const node document.getElementById(warp);node.removeEventListener(touchmove);
}) // 类似react 生命周期函数 componentWillUnmount
render(div idwarp onTouchEnd{onTouchEnd}// code...
/div)缓存。建议所有缓存都设置好过期时间。
3. 说说你对事件循环的理解
JavaScript 在设计之初便是单线程即指程序运行时只有一个线程存在同一时间只能做一件事 为什么要这么设计跟JavaScript的应用场景有关 JavaScript 初期作为一门浏览器脚本语言通常用于操作 DOM 如果是多线程一个线程进行了删除 DOM 另一个添加 DOM此时浏览器该如何处理 为了解决单线程运行阻塞问题JavaScript用到了计算机系统的一种运行机制这种机制就叫做事件循环Event Loop
事件循环Event Loop
在JavaScript中所有的任务都可以分为
同步任务立即执行的任务同步任务一般会直接进入到主线程中执行异步任务异步执行的任务比如ajax网络请求setTimeout 定时函数等
同步任务与异步任务的运行流程图如下 从上面我们可以看到同步任务进入主线程即主执行栈异步任务进入任务队列主线程内的任务执行完毕为空会去任务队列读取对应的任务推入主线程执行。上述过程的不断重复就是事件循环
微任务
一个需要异步执行的函数执行时机是在主函数执行结束之后、当前宏任务结束之前 常见的微任务有
Promise.thenMutaionObserverObject.observe已废弃Proxy 对象替代process.nextTickNode.js
宏任务
宏任务的时间粒度比较大执行的时间间隔是不能精确控制的对一些高实时性的需求就不太符合
常见的宏任务有
script (可以理解为外层同步代码)setTimeout/setIntervalUI rendering/UI事件postMessage、MessageChannelsetImmediate、I/ONode.js
这时候事件循环宏任务微任务的关系如图所示 按照这个流程它的执行机制是
执行一个宏任务如果遇到微任务就将它放到微任务的事件队列中当前宏任务执行完成后会查看微任务的事件队列然后将里面的所有微任务依次执行完
4. JavaScript中执行上下文和执行栈是什么
简单的来说执行上下文是一种对Javascript代码执行环境的抽象概念也就是说只要有Javascript代码运行那么它就一定是运行在执行上下文中 执行上下文的类型分为三种
全局执行上下文只有一个浏览器中的全局对象就是 window 对象this 指向这个全局对象函数执行上下文存在无数个只有在函数被调用的时候才会被创建每次调用函数都会创建一个新的执行上下文Eval 函数执行上下文 指的是运行在 eval 函数中的代码很少用而且不建议使用
执行栈也叫调用栈具有 LIFO后进先出结构用于存储在代码执行期间创建的所有执行上下文 当Javascript引擎开始执行你第一行脚本代码的时候它就会创建一个全局执行上下文然后将它压到执行栈中 每当引擎碰到一个函数的时候它就会创建一个函数执行上下文然后将这个执行上下文压到执行栈中 引擎会执行位于执行栈栈顶的执行上下文(一般是函数执行上下文)当该函数执行结束后对应的执行上下文就会被弹出然后控制流程到达执行栈的下一个执行上下文
5. 说说你对闭包的理解以及闭包使用场景
一个函数和对其周围状态lexical environment词法环境的引用捆绑在一起或者说函数被引用包围这样的组合就是闭包closure 也就是说闭包让你可以在一个内层函数中访问到其外层函数的作用域 在 JavaScript 中每当创建一个函数闭包就会在函数创建的同时被创建出来作为函数内部与外部连接起来的一座桥梁
下面给出一个简单的例子
function init() {var name Mozilla; // name 是一个被 init 创建的局部变量function displayName() { // displayName() 是内部函数一个闭包alert(name); // 使用了父函数中声明的变量}displayName();
}
init();displayName() 没有自己的局部变量。然而由于闭包的特性它可以访问到外部函数的变量
使用场景 任何闭包的使用场景都离不开这两点
创建私有变量延长变量的生命周期 一般函数的词法环境在函数返回后就被销毁但是闭包会保存对创建时所在词法环境的引用即便创建时所在的执行上下文被销毁但创建时所在词法环境依然存在以达到延长变量的生命周期的目的 具体描述请点击此链接
6. 你是怎么理解ES6中 Promise的使用场景有哪些
Promise译为承诺是异步编程的一种解决方案比传统的解决方案回调函数更加合理和更加强大 在以往我们如果处理多层异步操作我们往往会像下面那样编写我们的代码
doSomething(function(result) {doSomethingElse(result, function(newResult) {doThirdThing(newResult, function(finalResult) {console.log(得到最终结果: finalResult);}, failureCallback);}, failureCallback);
}, failureCallback);阅读上面代码是不是很难受上述形成了经典的回调地狱 现在通过Promise的改写上面的代码
doSomething().then(function(result) {return doSomethingElse(result);
})
.then(function(newResult) {return doThirdThing(newResult);
})
.then(function(finalResult) {console.log(得到最终结果: finalResult);
})
.catch(failureCallback);瞬间感受到promise解决异步操作的优点
链式操作减低了编码难度代码可读性明显增强
下面我们正式来认识promise
状态
promise对象仅有三种状态
pending进行中fulfilled已成功rejected已失败
特点
对象的状态不受外界影响只有异步操作的结果可以决定当前是哪一种状态一旦状态改变从pending变为fulfilled和从pending变为rejected就不会再变任何时候都可以得到这个结果
流程
认真阅读下图我们能够轻松了解promise整个流程
具体描述请点击此链接
7. js 中的倒计时怎么实现纠正偏差
在前端实现中我们一般通过 setTimeout 和 setInterval 方法来实现一个倒计时效果。但是使用这些方法会存在时间偏差的问题这是由于 js 的程序执行机制造成的setTimeout 和 setInterval 的作用是隔一段时间将回调事件加入到事件队列中因此事件并不是立即执行的它会等到当前执行栈为空的时候再取出事件执行因此事件等待执行的时间就是造成误差的原因。
一般解决倒计时中的误差的有这样两种办法
1第一种是通过前端定时向服务器发送请求获取最新的时间差以此来校准倒计时时间。
2第二种方法是前端根据偏差时间来自动调整间隔时间的方式来实现的。这一种方式首先是以 setTimeout 递归的方式来实现倒计时然后通过一个变量来记录已经倒计时的秒数。每一次函数调用的时候首先将变量加一然后根据这个变量和每次的间隔时间我们就可以计算出此时无偏差时应该显示的时间。然后将当前的真实时间与这个时间相减这样我们就可以得到时间的偏差大小因此我们在设置下一个定时器的间隔大小的时候我们就从间隔时间中减去这个偏差大小以此来实现由于程序执行所造成的时间误差的纠正。
8. 怎么使用 js 实现拖拽功能
一个元素的拖拽过程我们可以分为三个步骤:
第一步是鼠标按下目标元素第二步是鼠标保持按下的状态移动鼠标第三步是鼠标抬起拖拽过程结束
这三步分别对应了三个事件mousedown 事件mousemove 事件和 mouseup 事件。只有在鼠标按下的状态移动鼠标我们才会执行拖拽事件因此我们需要在 mousedown 事件中设置一个状态来标识鼠标已经按下然后在 mouseup 事件中再取消这个状态。在 mousedown 事件中我们首先应该判断目标元素是否为拖拽元素如果是拖拽元素我们就设置状态并且保存这个时候鼠标的位置。然后在 mousemove 事件中我们通过判断鼠标现在的位置和以前位置的相对移动来确定拖拽元素在移动中的坐标。最后 mouseup 事件触发后清除状态结束拖拽事件。
9. 异步编程有哪些实现方式
js 中的异步机制可以分为以下几种
第一种最常见的是使用回调函数的方式使用回调函数的方式有一个缺点是多个回调函数嵌套的时候会造成回调函数地狱上下两层的回调函数间的代码耦合度太高不利于代码的可维护。
第二种是 Promise 的方式使用 Promise 的方式可以将嵌套的回调函数作为链式调用。但是使用这种方法有时会造成多个 then 的链式调用可能会造成代码的语义不够明确。
第三种是使用 generator 的方式它可以在函数的执行过程中将函数的执行权转移出去在函数外部我们还可以将执行权转移回来。当我们遇到异步函数执行的时候将函数执行权转移出去当异步函数执行完毕的时候我们再将执行权给转移回来。因此我们在 generator 内部对于异步操作的方式可以以同步的顺序来书写。使用这种方式我们需要考虑的问题是何时将函数的控制权转移回来因此我们需要有一个自动执行 generator 的机制比如说 co 模块等方式来实现 generator 的自动执行。
第四种是使用 async 函数的形式async 函数是 generator 和 promise 实现的一个自动执行的语法糖它内部自带执行器当函数内部执行到一个 await 语句的时候如果语句返回一个 promise 对象那么函数将会等待 promise 对象的状态变为 resolve 后再继续向下执行。因此我们可以将异步逻辑转化为同步的顺序来书写并且这个函数可以自动执行。
10. JavaScript脚本延迟加载的方式有哪些
延迟加载就是等页面加载完成之后再加载 JavaScript 文件。 js 延迟加载有助于提高页面加载速度。
一般有以下几种方式 defer 属性 给 js 脚本添加 defer 属性这个属性会让脚本的加载与文档的解析同步解析然后在文档解析完成后再执行这个脚本文件这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的但是在一些浏览器中可能不是这样。 async 属性 给 js 脚本添加 async 属性这个属性会使脚本异步加载不会阻塞页面的解析过程但是当脚本加载完成后立即执行 js 脚本这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的一般不会按照代码的顺序依次执行。 动态创建 DOM 方式 动态创建 DOM 标签的方式可以对文档的加载事件进行监听当文档加载完成后再动态的创建 script 标签来引入 js 脚本。 使用 setTimeout 延迟方法 设置一个定时器来延迟加载js脚本文件 async 属性 给 js 脚本添加 async 属性这个属性会使脚本异步加载不会阻塞页面的解析过程但是当脚本加载完成后立即执行 js 脚本这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的一般不会按照代码的顺序依次执行。 动态创建 DOM 方式 动态创建 DOM 标签的方式可以对文档的加载事件进行监听当文档加载完成后再动态的创建 script 标签来引入 js 脚本。 使用 setTimeout 延迟方法 设置一个定时器来延迟加载js脚本文件 让 JS 最后加载 将 js 脚本放在文档的底部来使 js 脚本尽可能的在最后来加载执行。