Loading

1.17面试题整理

js的异步机制是怎样的?

特点

  • js是单线程语言(能提高效率。作为浏览器脚本语言,js的主要用途是与用户互动,操作DOM。而这也就决定它只能为单线程,否则会带来很复杂的同步问题),浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。

  • 浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。

  1. JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。

  2. GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。

  3. 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。

  4. 定时触发器线程

  5. 异步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 ]

如果该对象不是一个数组,那么用这两种方法转换出的数组为空

posted @ 2021-01-17 16:52  不吃苦瓜^  阅读(66)  评论(0编辑  收藏  举报