本期精读的文章是：

[Glossary of Modern JavaScript Concepts: Part 1](https://auth0.com/blog/glossary-of-modern-javascript-concepts/)

[Glossary of Modern JavaScript Concepts: Part 2](https://auth0.com/blog/glossary-of-modern-javascript-concepts-part-2/)


# 1 引言

我为什么要选这篇文章呢？

之所以选这篇文章, 是因为非常认同作者写这两篇文章的原因. 作者在文中说, 现代 JavaScript 的很多概念和思想在快速被传播和扩展, 很多新概念出现在前端相关的博客和文档中, 这些概念对于很多前端开发人员来说, 仍然很陌生. 因此我们有必要来学习一下现代的这些 JavaScript 的概念, 看这些概念在现在 JavaScript 的库或应用中是怎么被使用的.

# 2 内容概要

文章讲了很多现代 JavaScript 中的概念, 罗列如下:

## 纯函数和副作用
在了解纯函数之前, 首先要了解副作用. 副作用是指改变了其作用域外的状态. 副作用的举例有调用了一个 API, 操作了一个 DOM 节点, 弹出了一个弹窗, 或者改变了一条数据等.

而纯函数则是指 函数的返回值仅仅由参数决定, 当给同样的参数时, 返回值是固定的.

## Stateful 和 Stateless (有状态和无状态)
Stateless 无状态, 有点像纯函数, 不管理自己的数据或状态, 结果取决于参数. 而 Stateful, 有状态, 指的是函数自己有自己的运行状态, 可以修改自己的状态. 在现代 JavaScript 开发中, 处理状态, 显得很重要.

## 可变对象与不可变对象

可变对象与不可变对象概念很清楚, 可变对象指的是在创建后值仍可以被改变, 不可变对象指的是创建后值无法被改变.

相比于其他语言, 可变对象与不可变对象在 JavaScript 中更加模糊, 当你了解函数式编程时, 你会听到很多不可变对象的好处. 在 JavaScript 中, 你可以通过 Object.freeze(obj), 让一个对象变得不可变, 但是注意这是浅层的冻结对象, 如果有一个属性的值是个对象, 那这个对象中的属性是可以被修改的. 现在 JavaScript 也出现了 npm deep-freeze , Immutable.js 这些库来帮助你在 JavaScript 中实现不可变对象.

## Imperative and Declarative Programming(命令式和声明式编程)
命令式编程, 描述一段代码的逻辑怎么被显式调用去改变程序的状态. 声明式编程, 描述一段代码的逻辑, 而不需要描述如何完成这段逻辑.

JavaScript 可以同时被写为命令式和声明式编程方式, 但是随着函数式编程的兴起, 声明式编程将变得更加普遍.

## 高阶函数
函数作为 JavaScript 的一等公民, 可以跟普通数据类型一样, 被存储, 或者被作为值传参. 而高阶函数就是一种函数 可以接收另外一个函数作为入参, 或者返回一个函数作为结果.

## 函数式编程 FP
上面我们了解的 纯函数, 无状态, 不可变对象, 命令式编程, 和高阶函数, 都是很重要的函数式编程组成. 函数式编程通过以下方式包含上述概念:

- 关键函数实现使用纯函数, 没有副作用.
- 数据不可变
- 函数 无状态
- 声明式代码去管理副作用和执行命令式编程

## Hot and Cold Observables
Observables 和数组类似, 只不过数组是被保存在内存中, 而 Observables 的每一个元素则是异步加入进来. 我们可以订阅这些 observables.

Hot Observables 容易会被执行, 即使我们没有订阅它们. 比如说 用户的操作界面的 按钮点击事件, 鼠标移动, 窗口大小改变, 这些都是 Hot Observables. 而 cold observable 则是需要我们去订阅, 并且会在我们订阅的时候开始执行.

## 响应式编程 RP
响应式编程, 可以看作是面向异步事件流的编程, 声明式的, 表述去做什么, 而不是怎么做.

## 函数式响应型编程 FRP
函数式响应型编程简而言之,就是对事件或者行为给予声明式的反馈. FRP 具有两个很明显的特点:

- 函数或者类型有明确的定义
- 操作的是连续变化的值

## 作用域和闭包

闭包作为最常见的面试题经常被提及, 但是很多资深的前端开发都解释不清楚闭包, 即使他们理解闭包. 作者首先介绍了全局作用域和局部作用域, 作用域作为许多 JS 开发人员最开始学习的知识, 理解作用域对于编写优秀的代码至关重要.

闭包的形成在于, 当一个在函数内声明的函数可以引用外部函数的局部变量. 就形成了闭包.

## 单向数据流和双向数据流

随着现在各种 SPA 框架的兴起, 理解数据流概念, 对于现在 JS 开发者越来越重要, React 被认为是单向数据流的典范, 使用 Model 作为唯一的数据来源, 控制 View 的渲染. 在 View 层用事件的方式通知 Model 更新, 在反应到 View 层的变化上.  数据沿着一个方向流动, UI 永远不会更新 Model, 而是通过事件或者 setState 方法.

在双向数据绑定中, 数据是在两个方向上流动的, JS 可以更新 Model 数据, View 层 也可以更新 Model 数据. AngularJs 的 1.x 版本是双向数据流的典型实现.

早在 2009 年, 双向绑定是 Angualr 最受欢迎的特性之一, 但是 Angular 把这一特性抛弃了. 现在很多流行的框架和库都使用了单向数据流(React，Angular，Inferno，Redux 等). 单向数据流倡导的是清晰的架构, 数据流动更加清晰和易管理.  对于单向数据流来说说了点 View 自动更新数据的便利, 但也得到了清晰的数据流.

## JS 框架中的变化侦测: 脏检查, getter 和 setter, 虚拟 DOM
变化侦测对于现代 SPA 应用来说很重要. 当用户更新一些内容时, 应用必须以一种方法知道这种变化, 并做出反应更新.

AngularJS 1.x 使用的是脏检查的方式, 具体做法是对 View 中涉及到的 Model 进行深度比较. 脏检查的优点在于它的简单和可预测, 不涉及到 API 和对象的变更. 但是正因为涉及到大量比较, 也很低效.

Ember 和 Backbone 是使用 getters 和 setters 来做变化侦测, 这样涉及到数据修改时, 都会触发变更事件. 而 React 是使用了虚拟  Dom 来做变化侦测, React 通过 setState 方法来通知变更, 使用虚拟 Dom 来比较是否发生了数据变化.

## Web Components 组件
Web 组件是 Web 平台上可复用的基础组件, 而 Web Components 则定义了一些规范来实现这些可复用组件.
规范包括:
- 自定义元素
- HTML Template
- Shadow Dom
- HTML imports 引入

Web Components 本身并不能代替 SPA 框架的功能, 但是它的想法和核心概念, 在很多 SPA 框架中都有体现.

## Smart 和 Dumb 组件
现在 Web 的开发严重依赖组件, 而很多时候我们把组件分成 Smart 组件和 Dumb 组件.

Smart 组件, 又叫容器组件, 在组件内处理各种业务逻辑, 通常也管理 Dumb 组件,响应 Dumb 组件的事件.

Dumb 组件, 又叫展示组件, 通常被写成纯函数, 依赖于外部的数据和方法, 专注于展现数据.

## JIT 编译
Just-In-time（JIT）编译指的是代码的运行时, 被编译成机器代码的过程. 在 JavaScript 运行时, JIT 能够找到代码的特定模式, 而这些模式可以让 JavaScript 更快的被执行.

## AOT 编译
Ahead-Of-Time(AOT), 指的是编写的代码在运行之前, 被翻译成机器代码的过程. AOT 给 tree shaking 带来了可能, 使用 AOT 预编译, 对于生产环境下的代码有以下好处:

- 更少的异步请求, 模板和样式内联在 JS 内
- 更小的体积
- 更早的检查到模板错误
- 更好的安全性

## Tree Shaking
Tree Shaking 是指打包 JS 模块时, 通过对代码的静态分析, 排除掉不用的代码的机制.

Tree Shaking 技术建立在 ES2015 模块的, import 和 export 上, 支持我们导入特定的内容,而不是整个库.

```plain
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
```

这样我们只导入了 BehaviorSubject, 而没有导入整个 Rxjs 库.
# 3 精读
文中讲到的现代 JavaScript 已经很多了, 再对理解的现代 JavaScript 补充几条:

## Dependent injection（依赖注入）

通过控制反转，父级不需要关心子实现细节，将子类可能用到的实例都初始化好，由子类决定引入哪些依赖。还有一个好处是维持了单实例，这一点在数据流中尤为重要，如果 store 不是单例的，那数据流必然乱了套，既希望传给子类使用，又要维持单例，依赖注入是很好的解决方案。

## Symbol Reflect Proxy
Symbol 是 ES6 中加入的一种新的数据类型, 每一个 Symbol 都是独一无二的, 不与其它 Symbol 重复.

ES6 中的 Proxy , 则是通过 Proxy 方法, 实现对于对象的一层拦截. 提供一种机制, 代理对象的操作.  而 Reflect 是一个内置的对象，它提供可拦截 JavaScript 操作的方法。方法与代理处理程序的方法相同。

这三篇文章非常详细介绍了这三位 API：[symbol](https://www.keithcirkel.co.uk/metaprogramming-in-es6-symbols/) [reflect](https://www.keithcirkel.co.uk/metaprogramming-in-es6-part-2-reflect/) [proxy](https://www.keithcirkel.co.uk/metaprogramming-in-es6-part-3-proxies/)

## Server rendering

前端对后端渲染的热度降了很多，主要是盲目跟风的氛围消停了，真正需要的团队已经稳定的用起来了。后端渲染的理念很新颖，一定程度帮助了 html 认识到自己的不足，就像 Angular, React, Vue 对 webComponents 的冲击一样，或许未来十年可以用上 ECMAScript 标准提供的功能，但业务不能等待技术，现在唯有不断折腾，直到被消灭或者招安。


# 4 总结
伴随着各种框架的热度, 理解这些现代 JavaScript 概念变得越来越重要, 大家可以以这个作为概览, 详细去学习和了解现代 JavaScript 的概念.


> 讨论地址是：[精读《现代 JavaScript 概览》 · Issue #35 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/35)

> 如果你想参与讨论，请[点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，每周五发布。

