这是我参与「第四届青训营」笔记创作活动的第3天
如何写 好 JavaScript
本篇笔记内容分为“写好JS的原则”和“从不同方向优化代码”两个部分
第一部分:各司其责、组件封装、过程抽象。
第二部分:从哪些方面改进以适应不同的使用场景。例:交通灯的切换
写好JS的一些原则
各司其责:让HTML、CSS和JavaScript职能分离。
组件封装:好的UI组件具备正确性、扩展性、复用性。
过程抽象:应用函数式编程思想。
一、各司其责
写一段JS,控制一个网页,让它支持浅色和深色两种浏览模式。
版本一: code.juejin.cn/pen/7108183…
版本二:
code.juejin.cn/pen/7108183… 版本二问题:用JavaScript控制CSS,修改时不方便,不便理解代码含义。
版本三:
重点:各司其责,改变样式由CSS实现。
通过这个案例,我们可以得到结论:
- HTML/CSS/JS 应该各司其责
- 应当避免不必要由JS直接操作的样式
- 可以用class来表示状态
- 纯展示类交互寻求零JS方案
2、组件封装
组件是指Web页面上抽出来一个个包含模板(HTML)、功能(JS)和样式(CSS)的单元。
好的组件具备封装性、正确性、扩展性、复用性。
例:用原生JS写一个电商网站的轮播图,应该怎么实现?
结构设计:HTML。轮播图是典型的列表结构,我们可以使用无序列表ul元素来实现。
表现效果:CSS。
- 使用CSS绝对定位将图片重叠在同一个位置
- 轮播图切换的状态使用修饰符(modifier)
- 轮播图的切换动画使用CSS transition
行为设计:JS。
API(功能)
- getSelectedItem()
- getSelectedItemIndex()
- slideTo()
- slideNext()
- slidePrevious()
Event(控制流)
- 使用自定义事件来解耦
问题:构造函数代码复杂;控制点与组件不灵活
改进:插件化
解耦
- 将控制元素抽取成插件
- 插件与组件之间通过依赖注入方式建立联系
改进:模板化
解耦
- 将HTML模板化,更易于扩展
改进:组件框架
抽象
- 将组件通用模型抽象出来
总结:
- 组件设计的原则:封装性、正确性、扩展性、复用性
- 实现组件的步骤:结构设计、展现效果、行为设计
- 三次重构:插件化、模板化、抽象化(组件框架)
思考:改进的空间:现在的设计是插件与组件分离,这个设计的好处是简单、扁平化的,但是它存在的问题是没有考虑父子组件,可以考虑把插件跟组件给融合在一起,子组件可以作为父主线的插件来使用。
2、目前只做了HTML的模板化,没有做CSS模板化。
过程抽象
1、用来处理局部细节控制的一些方法
2、函数式编程思想的基础应用
例:操作次数限制
- 一些异步交互
- 一次性的HTTP请求
高阶函数 Once
为了能够让“只执行一次”的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。
高阶函数 HOF
- 以函数作为参数
- 以函数作为返回值
- 常用于作为函数装饰器
Once、Throttle(节流函数)、Debounce【防抖】(超过一定时间后自动保存)、Consumer/2(延时调用)、Iterative(批量操作)。
编程范式
命令式与声明式
命令式:
let list = [1, 2, 3, 4];
let mapl = [];
for(let i = 0; i < list.length; i++) {
mapl.push(list[i] * 2);
}
声明式:
let list = [1, 2, 3, 4];
const double = x => x * 2;
list.map(double);
写代码最应该关注什么?
风格、效率、约定、使用场景、设计
Leftpad事件
事件本身的槽点:1、NPM模块粒度;2、代码风格;3、代码质量/效率
原代码:
function leftpad(str, len, ch) {
str = String(str);
var i = -1;
if (!ch && ch !== 0) ch = ' ';
len = len - str.length;
while (++i < len) {
str = ch + str;
}
return str;
}
- 代码更简洁、效率提升
function leftpad(str, len, ch) {
str = "" + str;
const padLen = len - str.length;
if(padLen <= 0) {
return str;
}
return (""+ch).repeat(padLen)+str;
}
- 性能更好
/*! https://mths.be/repeat v1.0.0 by @mathias */
'use strict';
var RequireObjectCoercible = require('es-abstract/2019/RequireObjectCoercible');
var ToString = require('es-abstract/2019/ToString');
var ToInteger = require('es-abstract/2019/ToInteger');
module.exports = function repeat(count) {
var O = RequireObjectCoercible(this);
var string = ToString(O);
var n = ToInteger(count);
// Account for out-of-bounds indices
if (n < 0 || n == Infinity) {
throw RangeError('String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity');
}
var result = '';
while (n) {
if (n % 2 == 1) {
result += string;
}
if (n > 1) {
string += string;
}
n >>= 1;
}
return result;
};
交通灯切换
交通灯的切换可以有多种实现方法。以下是我不太熟悉的一种
异步 + 函数式实现
code.juejin.cn/pen/7108196…
总结
通过这次的学习,收获了如何逐渐优化代码来实现相同的场景。了解到了算法对于JavaScript很重要,今后会在算法上多下功夫。