闭包
什么是闭包
闭包是词法作用域和执行栈下的自然产物。在JavaScript中有全局作用域、函数作用域、块级作用域(ES6),
词法作用域取决于书写的位置,对于函数作用域而言,它能够访问更外层的作用域中的变量,但是外部作用域却无法访问函数作用域中的变量,当一个函数能够访问另一个函数作用域中的变量时就产生了闭包,这通常是一个嵌套函数。
内存泄漏
闭包的缺点是容易导致内存泄漏,这是因为执行栈机制,执行栈是一种管理执行上下文的机制,在JavaScript中有全局执行上下文、函数执行上下文和eval执行上下文这三种执行上下文。对于闭包而言,当外层函数执行后就会将该函数压入执行栈中,而当执行内层函数时会将内层函数的执行上下文压入栈中,等内层函数执行完毕则依次出栈内层函数和外层函数,但是假如有变量一直保持着对内层函数的引用,此时内层函数就不会被释放,而由于执行栈的先进后出的规则,就会导致外层函数执行上下文也不会被释放。
// 执行完毕就出栈释放
function a(){
function b(){}
b()
}
a()
// 一直保持内层函数的引用
function a(){
return function b(){}
}
const c = a()
闭包的作用
- 封装
- 作用域的扩展(可以在外部作用域中访问函数作用域内的变量)
this
web component
web component 的目的是实现可复用的定制元素。web component与vue这类MVVM框架的组件非常相似,但是不同的地方在于,web component是原生支持的,浏览器可以识别它,不需要经过编译才能被浏览器识别。
web component有以下特点
- 原生支持,性能更好,迭代前景更好
- 无关框架,理论上任何基于js的框架都能使用
- 基于shadow DOM(影子DOM),保证组件功能和样式的私有性,实现真正的组件化
web component由三大部分组成
- 自定义元素。 可以在html使用自定义的标签,来实现UI。
- 影子DOM。创建影子DOM树,并附加到主文档DOM上,并保证内部功能和样式的私有话,防止内部与外部功能和样式冲突。
- HTML模板,使用
template
和slot
,使得元素结构能够被复用和定制。
stenciljs
stencil是ionic团队开发的一款用于开发基于web component的系统或组件的编译器。
优点
- 大量使用装饰器,学习成本低,尤其是如果有Vue这类MVVM框架的经验时,很容易上手
- 自动生成使用文档
- 原生支持typescript和tsx
- 提供多种框架集成的案例
- 基于rollup打包,比webpack速度快
- 集成e2e测试
最佳实践
尽量减少DOM结构
减少DOM结构不仅能够提升性能,更能使用户更方便地自定义样式。
穿透样式
- 当DOM结构比较简单使,使用inherit来继承Host上的样式
- 提供css变量来定制关键样式
- 尽可能地提供part,利用::part伪类元素选择器来定制样式(注意:part选择器无法选中子孙)
快速开发 vs 控制反转
对于不复杂且通用的组件逻辑,提供Prop来控制,对于复杂的组件功能,提供slot来实现。换句话说就是通过Prop来实现快速开发,通过slot(控制反转思想的实现)来提供定制组件的能力。
保留一个_this指向节点
component有时候会自行调用某些方法,例如message组件会在1.5s后调用destroy方法自动销毁,而这个时候就需要访问到对应到元素节点,这里有两种解决方法,一种是为元素节点设置一个id,并且每创建一个节点就让id++,并将id保存到this中,这样就可以通过document.querySelector('特定前缀' + this.id)
来访问真实节点;另一种方法是通过jsx ref,直接获取host到节点对象,并存储为this的一个属性上(_this)。
第二种方法比第一种好,因为第二种不需要为host设置id,避免覆盖了用户设置的id。
更新:stencil提供@Element装饰器,可以直接获取Host元素。
踩坑记录
this的指向
stencil采用class来创建组件,而在class中this的指向有多种情况。
通常情况下this是指向实例,但是当类方法是作为事件处理程序被调用时,this则指向当前节点。
解决方法是使用箭头函数或者bind绑定this为类实例。
@font-face
在影子DOM使用@font-face无法正常的加载字体,无论是本地还是网络加载。目前的解决方式是通过js在主文档中创建style标签进行加载,或者直接将css引入。