写好JS的原则
- 各司其责-分离HTML/CSS/JS的职能
- 组件封装-好的UI组件具备正确性、拓展性、复用性。
- 过程抽象-应用函数式编程思想
一、各司其责
写一段JS控制一个网页,支持夜间模式切换
- 版本一:
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if(e.target.innerHTML === '🌞') {
body.style.backgroundColor = 'black';
body.style.color = 'white';
e.target.innerHTML = '🌜';
} else {
body.style.backgroundColor = 'white';
body.style.color = 'black';
e.target.innerHTML = '🌞';
}
});
用JS做了CSS应该做的事情,混在一起写不方便后续的修改
- 版本二:
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if(body.className !== 'night') {
body.className = 'night';
} else {
body.className = '';
}
});
符合各司其责的原则
- 版本三:
<input id="modeCheckBox" type="checkbox">
<div class="content">
<header>
<label id="modeBtn" for="modeCheckBox"></label>
<h1>深夜食堂</h1>
</header>
<main>
......
</main>
</div>
#modeCheckBox {
display: none;
}
#modeCheckBox:checked + .content {
background-color: black;
color: white;
transition: all 1s;
}
仅用CSS控制样式,不使用JS
小结
- HTML/CSS/JS各司其职
- 应当避免不必要的由JS直接操作样式
- 可以用class来表示状态
- 纯展示类交互寻求零JS方案
二、组件封装
用原生JS写一个电商网站的轮播图
1、结构HTML
HTML,轮播图是一个典型的列表结构,可以用无序列表ul>li*4来实现。
<div id="my-slider" class="slider-list">
<ul>
<li class="slider-list__item--selected">
<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/490a9fda06c042b2b00d4505b324b996~tplv-k3u1fbpfcp-zoom-1.image"/>
</li>
<li class="slider-list__item">
<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b30362f1e345488a82fb4562b85aca89~tplv-k3u1fbpfcp-zoom-1.image"/>
</li>
<li class="slider-list__item">
<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/17a15493fd0f46f48fe51244098a8b8b~tplv-k3u1fbpfcp-zoom-1.image"/>
</li>
<li class="slider-list__item">
<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d4b18ffcb5447d192f264548ee0ff50~tplv-k3u1fbpfcp-zoom-1.image"/>
</li>
</ul>
</div>
2、表现CSS
- 使用CSS结对定位将图片重叠在同一位置
- 轮播图切换的状态使用修饰符(modifier)
- 轮播图的切换动画使用CSS transition
#my-slider{
position: relative;
width: 790px;
}
.slider-list ul{
list-style-type:none;
position: relative;
padding: 0;
margin: 0;
}
.slider-list__item,
.slider-list__item--selected{
position: absolute;
transition: opacity 1s;
opacity: 0;
text-align: center;
}
.slider-list__item--selected{
transition: opacity 1s;
opacity: 1;
}
3、行为JS
API的设计应保证原子操作,职能单一,满足灵活性
class Slider{
constructor(id){
this.container = document.getElementById(id);
this.items = this.container
.querySelectorAll('.slider-list__item, .slider-list__item--selected');
}
getSelectedItem(){
const selected = this.container
.querySelector('.slider-list__item--selected');
return selected
}
getSelectedItemIndex(){
return Array.from(this.items).indexOf(this.getSelectedItem());
}
slideTo(idx){
const selected = this.getSelectedItem();
if(selected){
selected.className = 'slider-list__item';
}
const item = this.items[idx];
if(item){
item.className = 'slider-list__item--selected';
}
}
slideNext(){
const currentIdx = this.getSelectedItemIndex();
const nextIdx = (currentIdx + 1) % this.items.length;
this.slideTo(nextIdx);
}
slidePrevious(){
const currentIdx = this.getSelectedItemIndex();
const previousIdx = (this.items.length + currentIdx - 1)
% this.items.length;
this.slideTo(previousIdx);
}
}
const slider = new Slider('my-slider');
slider.slideTo(3);
//setInterval(()=>{
// slider.slideNext();
// },2000);
行为:控制流
- 使用自定义事件来解耦
4、小结:基本方法
- 结构设计
- 展现效果
- 行为设计
- API (功能)
- Event (控制流)
5、重构:插件化
在JS代码中,一个方法一般来说最多只能有15行代码,超过了就需要重构。为了便于代码的修改和优化,引入了插件化。
解耦
- 将控制元素抽取成插件
- 插件与组件之间通过依赖注入方式建立联系
6、重构:模板化
将HTML模板化,更易于拓展
7、组件框架
抽象:
将组件通用模型抽象出来。
8、总结
-
组件设计的原则:封装性、正确性、扩展性、复用性
-
实现组件的步骤:结构设计、展现效果、行为设计
-
三次重构
- 插件化
- 模板化
- 抽象化(组件框架)
三、过程抽象
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
1、高阶函数
HOF
- 以函数作为参数
- 以函数作为返回值
- 常用于作为函数装饰器
function HOF0(fn) {
return function(...args) {
return fn.apply(this, args);
}
}
常用高阶函数
Once
function once(fn) {
return function(...args) {
if(fn) {
const ret = fn.apply(this, args);
fn = null;
return ret;
}
}
}
Throttle 节流函数
限制频率
function throttle(fn, time = 500){
let timer;
return function(...args){
if(timer == null){
fn.apply(this, args);
timer = setTimeout(() => {
timer = null;
}, time)
}
}
}
debounce 防抖
抖动时不保存信息,静止1s后保存信息
function debounce(fn, dur){
dur = dur || 100;
var timer;
return function(){
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, dur);
}
}
Consumer
function consumer(fn, time){
let tasks = [],
timer;
return function(...args){
tasks.push(fn.bind(this, ...args));
if(timer == null){
timer = setInterval(() => {
tasks.shift().call(this)
if(tasks.length <= 0){
clearInterval(timer);
timer = null;
}
}, time)
}
}
}
iterative 可迭代方法
function iterative(fn) {
return function(subject, ...rest) {
if(isIterable(subject)) {
const ret = [];
for(let obj of subject) {
ret.push(fn.apply(this, [obj, ...rest]));
}
return ret;
}
return fn.apply(this, [subject, ...rest]);
}
}
2、编程范式
- 命令式与声明式
一些提高JavaScript编写质量的建议:
- 理解基本语法和核心概念:熟悉JavaScript的基本语法、数据类型、运算符、条件语句、循环和函数等核心概念。了解JavaScript的面向对象编程(OOP)特性,如原型继承和构造函数。
- 使用一致的命名和代码风格:采用一致的命名约定和代码风格,使代码易于阅读和维护。使用有意义的变量和函数名,遵循驼峰命名法或下划线命名法。使用缩进、空格和注释来提高代码的可读性。
- 避免全局变量污染:尽量避免使用全局变量,因为全局变量容易导致命名冲突和代码耦合。使用模块化的方式组织代码,将功能封装在模块或命名空间中,以减少全局变量的使用。
- 异常处理:合理处理异常和错误情况,使用try-catch语句捕获和处理异常。避免在代码中使用过多的try-catch块,而是将异常处理放在合适的位置,以提高代码的可读性和性能。
- 避免重复代码:尽量避免重复的代码块,使用函数和循环来实现代码的重用。将常用的功能封装成函数或类,以便在需要时进行调用。
- 使用合适的数据结构和算法:根据问题的需求选择合适的数据结构和算法。了解数组、对象、集合、映射等数据结构的特性和适用场景。学习和应用常见的算法,如排序、搜索和遍历算法,以提高代码的效率和性能。
- 优化性能:注意代码的性能问题,避免不必要的循环和递归,减少DOM操作的次数,合并和压缩JavaScript文件,使用异步编程和延迟加载等技术来提高页面加载和执行速度。
- 测试和调试:编写测试用例来验证代码的正确性和稳定性。使用浏览器的开发者工具进行调试,利用断点、日志和性能分析工具来定位和解决问题。
- 持续学习和更新:JavaScript是一门不断发展和演进的语言,保持学习的态度,关注最新的语言特性和最佳实践。参与开源项目、阅读优秀的代码和技术文章,与其他开发者交流和分享经验。