1. 说说你所知道的在JS中的数据类型:
      • 引用值: Array, Object, Set, Map等
      • 原始值: Number, String, Boolean, undefined, null, Symbol
    1. 说说在JS中引用值和原始值有什么区别?
      • 原始值存储在栈中, 当把一个原始值传递给变量的时候, 传递的是值的副本
      • 引用值存储在堆中, 当把一个引用值传递给另一个变量的时候, 传递的是值的指针, 相当于是门牌号, 当你修改一个引用值中的数据时, 另一个变量也会受到影响
      • 以上的话总结一下就是: 引用传址, 原始传值
    1. 说说在JS中栈(stack)和堆(heap)的区别:
      • 栈因为遵循的是有规律的先进后出的特点, 所以栈的存取速度比较快, 而且容易实现内存回收, 不过就是有点不太灵活
      • 堆的特点就是使用灵活, 因为他就等于是一块区域, 每个变量的存取就是在堆里给他划分一块空间, 空间还可以动态的删除和增加, 也正是因为这样太过于灵活, 导致查询速度会比较慢
    1. 聊聊IIFE(Immediately Invoked Function Expression)
      • 立即执行函数: 当一个函数做为立即执行函数的时候, 读到他这一行代码, 立马就会执行这个函数, 且执行完毕后立即断开该函数的AO连接
      • 立即执行函数的作用如下:
        • 创建一个独立的作用域, 这个作用域里的变量外部是访问不到的, 这样就可以避免变量污染
        • 实现闭包和私有数据, 实现“模块化”
    • 顺带扩展一下, JS中我们一般常用的书写立即执行函数的写法有(function(){})()(function() {}()) 两种, 但其实想要实现一个IIFE, 远不止这两种方式, 因为在JS中, 我们首先要明确一点, 只要是表达式就可以被执行, 而IIFE也不是ECMAScript的标准, 而是大家发现的一种编程技巧, 由于JS规定了只要是表达式就可以被执行, 所以我们只需要想方设法把一个函数声明变成表达式就可以了
    1. 聊聊作用域
      • 定义: 变量和函数能够被访问到的区域
      • 在JS中, ES6之前, 我们的作用域分为函数作用域和全局作用域两种, ES6以后, 我们多出来了一个块级作用域
      • 往深一点讲的话, 就是每个JS函数都是一个对象, 对象上有一个[[scope]]属性, 这个属性里面装的就是作用域, 其实也是这个函数的执行期上下文(也是我们常说的AO对象)
        • 当函数执行前一刻, 他会创建一个执行期上下文, 并直接开始走预编译3部曲:
          1. 找变量和形参名, 将变量和形参名作为AO对象的属性,属性值为undefined
          2. 形参实参相统一, 把实参值赋予AO对象中已经存储的形参属性
          3. 找函数声明, 将函数声明的名放入到AO对象中, 值赋予函数体
    1. - 我们还漏了一些东西, AO对象创建的时候(且在预编译之前)里面就已经有一些东西了, 比如arguments, 同时还存在了一个东西就是父级的执行期上下文
    2. - 当函数调用完毕, 他的执行期上下文就会被销毁
    3. - 而我们上面说的子集会攥着父级的作用域(执行期上下文), 这好像一个链条关系, 所以我们也称之为作用域
    1. 聊聊闭包
      • 当内部函数被保存到了外部的时候, 就造成了闭包, 闭包会导致原有的作用域链不被释放, 造成内存泄露
      • 闭包的作用大致如下:
        • 实现公有变量(比如用闭包实现一个累加器)
        • 做缓存结构(原理和实现公有变量差不多)
        • 隔离作用域实现“模块化”, 减少全局污染
    1. 聊聊原型和原型链
      • 原型是function上的一个属性, 他定义了由构造函数构造出来的对象的公有祖先, 通过该构造函数构造出来的对象, 可以集成原型的属性和方法, 原型也是对象
      • 所有的对象都具有一个隐式原型__proto__, 默认情况下, 隐式原型指向创建该对象的构造函数的prototype
      • 当我们访问一个对象上的成员的时候:
        1. 看看对象自身是否拥有该成员, 如果有的话直接使用
        2. 看看对象的__proto__上是否拥有该成员, 如果有直接使用
        3. 由于该对象的__proto__也是一个对象, 所以他如果没有的话, 他又会去找他的__proto上有没有, 依次类推, 最终找到Object.prototype, 这就是原型链
        4. 但是并不是所有对象的原型链的最顶端都是Object.prototype, 有一个例外, 就是通过Object.create(null)创建的对象, 隐式原型会直接指向null
    • 由于原型(prototype)是函数上的属性, 又由于prototype自身也是对象, 那对象又是Object构造函数创建出来的, 所以这里存在一个先有鸡还是先有蛋的问题
      • Function.__proto__ === Function.prototyp
      • Object.__proto__ === null
    1. 说说this指向的问题:
      • 函数预编译过程中的this指向了window
      • 全局作用域中this指向window
      • 谁调用的函数, this就指向谁, 如果是自执行, 则指向window
      • call, apply, bind可以改变函数的this指向
      • es6箭头函数的this来源于该函数出生时向上寻找有this的父级作为自己的this
    1. 说说浏览器的Event Loop
      • 事件循环本身是为了去协调事件, 用户交互, 脚本, 渲染引擎, 网络等工作的有序进行的
      • 为什么需要事件循环?因为在JS是单线程的, 那就意味着同一时间段只能做一件事, 我打个比方, 假设JS的主线程去监听用户的点击事件的话(甚至一切IO操作), 那么在监听事件到触发事件的这个过程中, 我们将什么JS代码都不能执行, 这是非常恐怖的
      • 我们可以认为JS的主线程是只会走同步代码的, 那么IO操作就需要交给其他的调度线程去做, 然后这些调度线程根据事件循环来确定优先级, 换句话说事件循环本身就是一种任务约定, 他规定了如下:
        1. 当JS的主线程有需要执行的任务时, 会将所有主线程的任务全部执行完毕
        2. 当主线程的任务执行完毕以后, JS会去查看其他线程是否有待执行的任务, 比如setTimeout被计时器线程读秒结束以后会将回调函数放入宏任务队列, 这个时候JS看到了就会将宏任务队列中的任务拉进主线程继续执行, 执行完毕以后又会去查看其它线程的情况, 如此往复, 这就是事件循环
        3. 浏览器端的事件循环规定了宏任务和微任务一说:
          • setTimeout, setInterval进入宏任务
          • MutationObserver, Promise进入微任务
            微任务队列的任务始终会比宏任务队列的任务先被选入JS主线程进行执行