1.17面试题整理
js的异步机制是怎样的?
特点
-
js是
单线程
语言(能提高效率。作为浏览器脚本语言,js的主要用途是与用户互动,操作DOM。而这也就决定它只能为单线程,否则会带来很复杂的同步问题),浏览器只分配给js一个主线程
,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。 -
浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。
-
JavaScript引擎
是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。 -
GUI渲染线程
负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。 -
事件触发线程
,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。 -
定时触发器线程
-
异步http请求线程
机制
首先明确js中任务的分类
- 所有任务可以分成两种,一种是
同步任务
(synchronous),另一种是异步任务
(asynchronous)。 - 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
- 异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
机制如下
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
简述一下event loop
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
任务主要分为了两种
- 宏任务:script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
- 微任务:Promise、 MutaionObserver、process.nextTick(Node.js环境)
宏任务 > 所有微任务 > 宏任务 如此循环就形成了event loop
,其中,每轮执行 一个宏任务
和所有的微任务
什么是提升,有什么作用
首先要明确在ES6之前,JavaScript没有块级作用域,只有全局作用域
和函数作用域
。
js中提升主要分为了两类
-
变量提升 使用 var 声明的变量会被提升到
作用域的
顶部 -
函数提升 函数也会被提升,并且
优先于
变量提升。但是使用表达式定义的不会被提升
f();
var f = function (){};
// TypeError: undefined is not a function
提升存在的根本原因就是为了解决函数间互相调用的情况
什么是暂时性死区
在代码块内,使用let或者const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
在声明前使用let/const声明的变量会出现什么问题
会报错 因为有暂时性死区的存在
判断一个对象是数组的方法有哪些
题干的意思就是判断一个对象是否是伪数组
就转换成了判断一个对象能不能转换成数组
1.使用Array.from()
2.使用[].slice().call()
let obj={
"0":"zhang",
"1":18,
/* length:2 */
}
let obj1={
"0":"zhang",
"1":18,
length:2
}
console.log([].slice.call(obj)) //[]
console.log([].slice.call(obj1)) //[ 'zhang', 18 ]
console.log(Array.from(obj)) //[]
console.log(Array.from(obj1)) //[ 'zhang', 18 ]
如果该对象不是一个数组,那么用这两种方法转换出的数组为空