# 重要考察内容

# 说说你对闭包的理解?闭包使用场景

# 闭包产生的原因

Js 最大的缺点就是没有类,尤其是es5,自身没有面向对象,变量和函数通常都是写在同一个空间中,变量重名—污染,函数名重名—污染 而闭包能够形成一个封闭的空间,可以避免污染,储存私有变量,存在函数里面 ,这个私有变量不会在函数运行完后被清理 ,可以像全局变量一样被使用,不会失效

# 什么是闭包

官方解释:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行

词法作用域是作用域的一种工作模型 嵌套函数被返回在外部执行,他的那么它会保留父级函数作用域链不被销毁(函数套函数) 在全局中必须接收返回函数作为变量储存;

# 优点

  1. 内容更封闭,保证命名不会冲突;
  2. 模块化开发—封闭的模块化环境

# 缺点

闭包最大缺点就是会造成内存泄漏,存在堆中,不会被垃圾回收;

# 使用场景

  • 创建私有变量/延长变量的生命周期
  • 柯里化函数(柯里化的目的在于避免频繁调用具有相同参数函数的同时,又能够轻松的重用)
  • 模拟私有方法(例如计数器、延迟调用、回调等闭包的应用,其核心思想还是创建私有变量和延长变量的生命周期)

# JavaScript原型,原型链 ? 有什么特点?

# 原型对象

  • 每一个函数都有一个原型(prototype)属性,这个属性是一个指针,指向一个对象
  • prototype就是通过调用构造函数而创建的那个对象实例的原型对象
  • 带来的好处:所有的对象实例共享原型所包含的属性和方法

# 原型链

  • 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找,没有则继续往上找,一直往上找,会形成一个链式结构,叫做“原型链”,原型链的终点就是null
  • 主要思想就是利用原型让一个引用类型继承另一个引用类型的对象和方法
  • js的继承主要就是通过原型链实现的

# 总结

  • 一切对象都是继承自Object对象,Object 对象直接继承根源对象null
  • 一切的函数对象(包括 Object 对象),都是继承自 Function 对象
  • Object 对象直接继承自 Function 对象
  • Function对象的__proto__会指向自己的原型对象,最终还是继承自Object对象

# Javascript如何实现继承?

# 原型链继承

涉及的构造函数、原型和实例,三者之间存在着一定的关系,即每一个构造函数都有一个原型对象,原型对象又包含一个指向构造函数的指针,而实例则包含一个原型对象的指针

# 构造函数继承(借助 call)

相比第一种原型链继承方式,父类的引用属性不会被共享,优化了第一种继承方式的弊端,但是只能继承父类的实例属性和方法,不能继承原型属性或者方法

# 组合继承

原型链继承+构造函数继承

# 原型式继承

  • 借助Object.create方法实现普通对象的继承
  • 实现的是浅拷贝,多个实例的引用类型属性指向相同的内存,存在篡改的可能

# 寄生式继承

在上面继承基础上进行优化,利用这个浅拷贝的能力再进行增强,添加一些方法

# 寄生组合式继承

  • 寄生组合式继承,借助解决普通对象的继承问题的Object.create 方法,在前面几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式
  • es6 class中的extend使用的就是类似寄生组合式的方法

# 说说JavaScript中的事件模型

# 事件

在HTML文档或者浏览器中发生的一种交互操作,使得网页具备互动性, 常见的有加载事件、鼠标事件、自定义事件等

# 事件流经历三个阶段

  • 事件捕获阶段(capture phase)
    • 事件捕获与事件冒泡相反,事件最开始由不太具体的节点最早接受事件, 而最具体的节点(触发节点)最后接受事件
  • 处于目标阶段(target phase)
  • 事件冒泡阶段(bubbling phase)
    • 事件冒泡是一种从下往上的传播方式,由最具体的元素(触发节点)然后逐渐向上传播到最不具体的那个节点,也就是DOM中最高层的父节点

# 事件模型分为三种

  • 原始事件模型(DOM0级)
  • 绑定速度快
  • DOM0级事件具有很好的跨浏览器优势,会以最快的速度绑定,但由于绑定速度太快,可能页面还未完全加载出来,以至于事件可能无法正常运行 只支持冒泡,不支持捕获
  • 同一个类型的事件只能绑定一次
  • 标准事件模型(DOM2级)
    • 三个过程:
      • 事件捕获阶段:事件从document一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
      • 事件处理阶段:事件到达目标元素, 触发目标元素的监听函数
      • 事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
  • IE事件模型(基本不用)

# 解释下什么是事件代理?应用场景?

事件代理,俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素,事件委托就是在冒泡阶段完成

事件委托,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,而不是目标元素

当事件响应到目标元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数

优点

  • 减少整个页面所需的内存,提升整体性能

# 事件循环eventloop

js是单线程的脚本语言,在同一时间,只能做同一件事,为了协调事件、用户交互、脚本、UI渲染和网络处理等行为,防止主线程阻塞,Event Loop方案应运而生

# 执行栈

当遇到异步任务(如ajax操作等)时,不可能一直等待异步完成,再继续往下执行,在这期间浏览器是空闲状态,显而易见这会导致巨大的资源浪费 执行任务队列中的某个任务,这个被执行的任务就称为执行栈

# 主线程

主线程规定现在执行执行栈中的哪个事件 当遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中,我们称之为任务队列(Task Queue) 当主线程将执行栈中所有的代码执行完之后,主线程将会去查看任务队列是否有任务。如果有,那么主线程会依次执行那些任务队列中的回调函数。 主线程循环:即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码。

# js 异步执行的运行机制

所有任务都在主线程上执行,形成一个执行栈。 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。 主线程不断重复上面的第三步

# 宏任务与微任务

异步任务分为 宏任务(macrotask)与 微任务 (microtask)

  • 宏任务
    • script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
  • 微任务
    • Promise、 MutaionObserver、process.nextTick(Node.js环境)

# 事件循环步骤

Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:

  • 执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
  • 检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
  • 更新render(每一次事件循环,浏览器都可能会去更新渲染)
  • 重复以上步骤