每天一个知识点

215 阅读8分钟

1.理解Event Loop

  1. JavaScript是单线。(原因:JavaScript的主要用途是与用户互动,以及操作DOM,这决定了它只能是单线程)
  2. 所有同步任务都在主线程上执行,形成一个执行栈。
  3. 主线程之外,还存在一个"任务队列"(消息队列)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  4. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  5. 任务队列又分为微任务对列和宏任务对列,微任务队列执行完毕,才回去执行宏任务队列。
  6. 述过程会不断重复,也就是常说的Event Loop(事件循环)。

微任务: promise 的回调、node 中的 process.nextTick 、对 Dom 变化监听的 MutationObserver。

宏任务: script 脚本的执行、setTimeout ,setInterval ,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲 染等。

作者:JakeZhang
链接:juejin.cn/post/684490… 来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

总结:主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)

参考:JavaScript 运行机制详解:再谈Event Loop

2.作用域和作用域链

  • 作用域:
  1. Javascript中的作用域说的是变量的可访问性和可见性。也就是说整个程序中哪些部分可以访问这个变量,或者说这个变量都在哪些地方可见。
  2. Javascript使用词法作用域(静态作用域),这意味着变量的作用在编译阶段(定义时的作用域)就会被确定。
  3. 全局作用域:任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问
  4. 函数作用域:如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问。
  5. 块级作用域:在大括号中使用let和const声明的变量存在于块级作用域中。在大括号之外不能访问这些变量。
  • 作用域链:
  1. 作用域链就是用来查找变量的,作用域链是由一系列作用域串联起来的。(当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域。 如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错。)
  2. 变量按从内向外的查找
  3. 内层的变量可以屏蔽外层的同名变量

参考:讲清楚之javascript作用域

参考:理解Javascript的作用域和作用域链

3.闭包

function foo() {
  let a = 2
  function too() {
    console.log(a)
  }
  return too
}
foo()() // 2

一个函数执行后返回另一个可执行函数,被返回的函数保留有对它定义时外层函数作用域的访问权。foo()() 调用时依次执行了 foo、too 函数。too 虽然是在全局作用域里执行的,但是too定义在 foo 作用域里面,根据作用域链规则取最近的嵌套作用域的属性 a = 2。

优点:

  • 用于保存私有属性:将不需要对外暴露的属性、函数保存在闭包函数的父函数里,避免外部操作对值的干扰
  • 避免局部属性污染全局变量空间导致的命名空间混乱 缺点:
  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题

总结:闭包就是能够读取其他函数内部变量的函数,这里把闭包理解为函数内部定义的函数。

4.this指向

  • 正常情况下this的指向
  1. 纯粹的函数调用。(这是函数的最通常用法,属于全局性调用,因此this就代表全局对象)
  2. 作为对象方法的调用。(函数还可以作为某个对象的方法调用,这时this就指这个上级对象)
  3. 作为构造函数调用。(所谓构造函数,就是通过这个函数,可以生成一个新对象。这时,this就指这个新对象)
  • 怎么改变 this 的指向
  1. 使用 ES6 的箭头函数。(箭头函数的 this 始终指向函数定义时的 this,而非执行时。,箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”
  2. 在函数内部使用 _this = this
  3. 使用 apply、call、bind
    1. apply 和 call 基本类似,他们的区别只是传入的参数不同(call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。)
    2. bind 是创建一个新的函数,我们必须要手动去调用(bind 方法接受的是若干个参数列表)
  • 自己理解 this.X的变量和作用域变量不一样,this当前环境没有,他就不会向上查找了 总结:this就是函数运行时所在的环境对象。(this 永远指向最后调用它的那个对象)

参考:Javascript 的 this 用法

参考:JavaScript 的 this 原理

参考:this、apply、call、bind

5.MVC和MVVM

  • MVC (m:模型 v:视图 C:控制器) 特点:各部分之间的通信方式如下,所有通讯都是单向的 。
    1. View 传送指令到 Controller
    2. Controller 完成业务逻辑后,要求 Model 改变状态
    3. Model 将新的数据发送到 View,用户得到反馈
graph TD
View --> Controller --> Model --> View

总结:MVC模式的业务逻辑主要集中在Controller,而前端的View其实已经具备了独立处理用户事件的能力,当每个事件都流经Controller时,这层会变得十分臃肿。而且MVC中View和Controller一般是一一对应的,捆绑起来表示一个组件,视图与控制器间的过于紧密的连接让Controller的复用性成了问题。

  • MVVM(m:数据层 v:视图 vm:连接view和model的桥梁)
graph TD
ViewModel-->View --> ViewModel -->Model -->ViewModel 

在MVVM的框架下视图View和模型Model是不能直接通信的。它们通过ViewModel来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信。

  • 对比 MVVM不仅仅简化了业务与界面的依赖,还解决了数据频繁更新(频繁DOM操作),降低了应用的耦合,提高了代码的重用性

参考:浅谈 MVC 和 MVVM 模型

6.Vue2原理

1606e7eaa2a664e8.jpg

1._init:初始化生命周期、事件、 props、 methods、 data、 computed 与 watch 等。其中最重要的是通过 Object.defineProperty 设置 setter 与 getter 函数,用来实现「响应式」以及「依赖收集

2.compile:

  • parse:会用正则等方式解析 template 模板中的指令、class、style等数据,形成AST.
  • optimize:标记 static 静态节点,patch 的过程, diff 算法会直接跳过静态节点,从而减少了比较的过 程,优化了 patch 的性能。
  • generate:将 AST 转化成 render function 字符串的过程(render 的字符串(vnode节点))。

3.patch:核心 diff 算法,通过同层的树节点进行比较而非对树进行逐层搜索遍历的方式。

4.数据变更

  • setter

  • dep.notify()

  • watcher.update()

  • queueWatcher(this) // watcher把自身传进了queueWatcher()在queueWatcher方法中

  • queue.push(watcher) // 在push之前会检查queue中是否已有该watcher(比如:一个属性改变100次)

  • !waiting && waiting = true && nextTick(() => { // ... 执行queue中所有watcher的run }) (所以当改变一个值是,直接调用dom是拿不到最新的,只有当下一次tick完成才能拿到)

  • waiting= false

总结: 采用数据劫持结合发布-订阅模式,通过Object.defineProperty()方法劫持各个属性的set,get,在数据变动时发布消息给订阅者,触发相应监听回调。数据变化更新视图,视图变化更新数据。

参考:剖析 Vue.js 内部运行机制

7.原型和原型链

  1. 引用类型,都具有对象特性,即可自由扩展属性。

  2. 引用类型,都有一个隐式原型 __proto__ 属性,属性值是一个普通的对象。

  3. 引用类型,隐式原型 __proto__ 的属性值指向它的构造函数的显式原型 prototype 属性值。

  4. 当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型 __proto__(也就是它的构造函数的显式原型 prototype)中寻找。

总结:原型链的概念: 实例先从自身出发检讨自己,发现并没有 toString 方法。找不到,就往上走,找  构造函数的prototype属性,还是没找到。构造函数的 prototype也是一个对象,那对象的构造函数Object, 所以就找到了 Object.prototype下的 toString方法。这种寻找的过程就是原型链

参考:面不面试的,你都得懂原型和原型链