在日常的自我提升过程中,总会有些碎片化的知识点在经过一段时间的积累和沉淀后才能汇总出一个系统的方法论。固有此文不定期更新,专门用于汇总结论。
js基础
我们声明的
function
其实等价于Function
构造的实例,所以如function Person(){}
有Person.__proto__ === Function.prototype
为true
。一个
async function
在EL
中的执行顺序可以理解成:在内部的await
等到返回值前的代码块为Promise
中的声明块,是同步的。await
进行函数调用时,可以理解为一个Promise.resolve()
,其中若是函数,没有return
前的内容也是同步执行的。最后return
的内容和await
后的内容等价于resolve
后then
方法对应的回调(micro task)。Promise构造函数中,即便
resolve
了,后续的代码依旧会执行(正常入栈)。对于resolve
回调的这一句来讲可以理解为异步,后续的同步会先执行,e.g.
1 | let p = new Promise((resolve, reject) => { |
我们知道js的没有所谓指针一说,即便是引用类型也是将地址赋值给我们的变量。所以类似
var a = b = {}
这种,其实只是将两者都存储了该对象地址的访问,但是a
与b
间并没有关联关系(它们只是存了同一个地址)。那么当我们再次修改a
存储别的地址空间比如a = {name: 'Leo'}
,此时b
还是老样子{}
。js的动态性,就我个人直观感受而言主要体现在变量、函数声明的提升上,解析引擎会预先确认相互的关系(LHS、RHS)。
执行环境及作用域,过去理解其实就是一层套一层,逐层往外寻找,取最近一层的内容,理论上够用了但是还是不够系统。
根据红宝书的描述:我们运行JS的环境存在一个全局执行环境(浏览器的window
、Node的global
),另外每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境会被推入一个环境栈中,在函数执行完毕后,栈会将该推入环境弹出,将控制权返回给之前的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链,它保证了对执行环境中有权访问的所有变量和函数的访问有序性。而作用域链的前端,始终都是当前执行代码所在环境的变量对象。那这个变量对象是什么呢?当环境是函数时,会将活动对象作为变量对象(可以理解为入参那一块的空间,初始时是我们的arguments
对象)。然后再逐个去下个包含环境搜索,直到全局执行环境(同时也是作用域链中最后一个对象)。
- 接上面的内容,举2个例子:
1 | function sayMyName(name) { |
首先在函数执行前,变量和函数的声明都会被优先处理;执行函数时,活动对象AO
被创建。此时活动对象中的name
被传入的Tony
赋值,但是由于内部的函数声明提升,函数声明提升优先级最高,高于AO和内部变量提升,于是有第一个输出function name() {}
;另外还有个规则是对同一个变量的重复声明,会静默失败;接着由于赋值操作不会被提升,name
被赋值为Leo
然后输出Leo
,最后的输出中间没有影响因素,通输出Leo
。
1 | function sayMyName(name) { |
此处变量提升,但由于AO中已经存在声明的name
,所以静默失败不影响原值Leo
,遂先输出Leo
,之后name
被function () {}
覆盖,最终函数被调用根据作用域链查找,输出function () {//...}
。
- 再举两个比较有意思的作用域链查找:
1 | var b = 1; |
React
ref
不仅可以作用在HTML element
上,亦可作用在我们的component
上;React.createRef()
在使用时,注意只能作用于类class
,作用于函数组件会失效报warning
:
React.forwardRef
适用于函数组件,他接收一个render function
并返回一个新的React组件(HOC),主要应用在ref
需要向子组件内嵌套的元素传递的情景:
1 | // 官方文档 DEMO |
Reconciliation
是React中处理渲染性能优化的一部分,它通过几种策略成功将原本需要消耗 O(n3) 时间复杂度的DOM树转化工作优化到 O(n)。官方文档称其是一种启发式的策略,我们基于两个前提(几乎所有场景符合):
两个不同类型的元素将会产生不同的树型结构
开发者能够通过设置
key
告知调和过程哪些内容需要保留
常说的Diffing算法可以概括成下述几种策略:
- 不同类型的元素节点,如原生DOM、React Component发生改变时,以该变动节点为根的树将被全部销毁并重建新的树型结构。
- 相同类型的DOM节点,React会逐步比较其属性、文本差异,仅更新有差异的部分,而不会直接销毁一整部分。
- 相同类型的React Component节点,当更新时,其实例会保留,React通过
componentWillReceiveProps
、componentWillUpdate
更新嵌套的子内容,然后在render
中又对子内容递归应用DIFF。 - key可以规避一些场景的整体重建,通过局部移动或者插入的方式刷新内容。
受控与非受控组件是React表单处理下的概念,其最基本的逻辑是单一数据源。然而Form本身,就有数据关联的逻辑比如
value
、checked
这些属性,那要使其具有唯一控制源头,最自然的做法就是用state
控制:1
2
3
4
5
6
7
8
9
10// input
<input type="text" value={this.state.value} onChange={this.handleChange} />
// textarea
<textarea value={this.state.value} onChange={this.handleChange} />
// select
<select multiple={true} value={['B', 'C']}>
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
</select>另外对于
<input type="file" />
,由于其value
是只读的,所以file input
标签是一个铁非受控组件。相较于受控的state
,非受控组件就是对原生DOM表单的值获取了,我们可以通过ref
的current
属性来拿。值得注意的是,在React中一些原生组件的属性都被重写了,就像合成事件那样。所以value
如果不是受控组件的state
设置值,我们无法再对其设置初始值,可以用React提供的另一个属性defaultValue
来替代。简单点来说,就是React不用
state
或是用不了state
控制的表单元素是非受控的,反之则是受控的。基于单一数据源的哲学和可追溯的考虑,React推荐大多数场景使用受控组件。
@babel/plugin-proposal-class-properties
该babel
插件非常重要,可以说是我们在Class
中直接写=>
函数最关键的一步,因为它能够帮我们将函数直接绑定到生成的实例函数上(不是原型上),以下代码取自babel官方:
1 | class Bork { |
git flow
git rebase
一般有两个主流应用场景,其一是交互模式下多条commit
信息的整理合并;其二是替代使用git merge
进行分支合并,最后在对Master分支进行PR后,主分支就仍是一条干净的改动路线图。可以参考知乎这篇内容Git commits历史是如何做到如此清爽的?,其中Vue维护团队也是如此规范处理的。
工程化
husky
提供了开发者更易介入git hooks
的能力。我们主要在其中两个hooks
阶段操作:pre-commit
内进行ESLint
校验,发生错误会输出并退出git
流程;commit-msg
配合git-cz
进行规范化的提交message
模板配置。但是要是有人使用-n
这个参数就会直接跳过校验lint
的步骤…所以归根到底这种限制还是一种弱约束。真想严格统一上传代码的格式可以使用CI流程中的GIT HOOKS(区分于前面的git flow
,是一种自动化部署的流程)触发。package-lock.json
在不同npm
版本下的执行反馈不同(npm 5
后开始我们根据package.json
进行npm i
会默认生成一个package-lock.json
文件记录当时具体的各依赖版本号):5.0.x
版本,package.json
不管如何修改,都会严格根据lock
中的具体版本(初始生成的)下载依赖;
5.1.0 ~ 5.4.2
版本,npm i
会无视lock
的配置,即每次安装依赖都会根据semver
变动刷新lock
配置;
5.4.2 ~ future
版本,我们通过npm i
进行指定依赖安装(xxx@xxx
)时,若与package.json
中写入的不一致会重新覆盖其中的配置,并安装指定的依赖。lock
中之前初始生成的对应配置则不会改变。但如果在package.json
中手动修改了版本再npm i
,lock
中的配置就会对饮更新。npm
在没有lock
机制出现前似乎有这么一个维护关系的文件,npm-shrinkwrap.json
;本着兼容的关系,这个老配置优先级会高于pakcage-lock.json
。package-lock.json
不应写入.gitignore
,应当提交到git repo
上。可以通过项目级的.npmrc
配置package-lock=false
关闭lock
机制。另外对于yarn
和npm
两种安装方式,可以把不使用的一方写入.gitignore
,比如使用npm
,把yarn.lock
写入。参考知乎
npm
使用与依赖关系: