Day4 如何写好JavaScript | 青训营

67 阅读3分钟

编码原则 —— 各司其责

让HTML、CSS、和JS职能分离

示例 —— 深夜食堂

要求:点击按钮切换文字、和背景至黑夜模式

版本一:

const btn = document.getElementById('modeBtn')
btn.addEventListener('click', (e) => {
	const body = document.body
	if(e.target.innerHTML === 'sun.jpg') {
		body.style.backgroundColor = 'black';
		body.style.color = 'white';
		e.target.innerHTML = 'moon.jpg';
	} else {
		body.style.backgroundColor = 'white';
		body.style.color = 'black';
		e.target.innerHTML = 'sun.jpg';
	}
});
/* 用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 = '';
	}
});

版本三:

#modeCheckBox {
	display: none;
}

#modeCheckBox:checked + .content {
	background-color: black;
	color: white;
	transition: all 1s;
}
/* 这里几乎是只有样式更改,可以只使用CSS实现 */

结论

  • HTML/CSS/JS各司其责
  • 应当避免不必要的由JS直接操作样式
  • 可以用class来表示状态
  • 纯展示类交互寻求零JS方案

编码原则 —— 组件封装

好的UI组件具备正确性、拓展性、复用性

实现轮播图

结构:HTML

轮播图是一个典型的列表结构,可以使用无序列表<ul></ul>来实现

表现:CSS

  • 使用CSS绝对定位将图片重叠在同一个位置
  • 轮播图切换的状态使用修饰符(modifier)
  • 轮播图的切换动画使用CSS transition

行为:JS

API

  • Slider
    • +getSelectedItem( )
    • +getSelectedItemIndex( )
    • +slideTo( )
    • +slideNext( )
    • +slidePrevious( )

控制流

  • 使用自定义事件来解耦

基本方法

  • 结构设计
  • 展现效果
  • 行为设计
    • API (功能)
    • Event (控制流)

重构:插件化

  • 将控制元素抽取成插件
  • 插件与组件之间通过依赖注入方式建立联系
registerPlugins(...plugins) {
	plugins.forEach(plugin => plugin(this));
}

重构:模板化

将HTML模板化,更加易于拓展

模板化.png

抽象化(组件框架)

  • 将组件通用模型抽象出来

抽象化.png

总结

  • 组件设计的原则:封装性、正确性、扩展性、复用性
  • 实现组件的步骤:结构设计、展现效果、行为设计
  • 三次重构
    • 插件化
    • 模板化
    • 抽象化 (组件框架)

编码原则 —— 过程抽象

应用函数式编程思想
过程抽象:

  • 用来处理局部细节控制的一些方法
  • 函数式编程思想的基础应用

高阶函数

为了能够让只执行一次的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。

高阶函数(HOF)可以实现我们过程抽象的目标:

  • 以函数为参数
  • 以函数为返回值
  • 常用于作为函数装饰器
function once(fn) {
	return function(...args) {
		if(fn) {
			const ret = fn.apply(this, args);
			fn = null;
			return ret;
		}
	}
}
// 一个用于实现让某函数只执行一次的高阶函数

function HOF0(fn) {
	return fn.apply(this, args);
}
// 一个等价高阶函数

常用高阶函数

HOF

  • Once
  • Throttle // 节流函数
    function throttle(fn, time = 500) {
    	let timer;
    	return function(...args) {
    		if (timer == null) {
    			fn.apply(this, args);
    			timer = setTimeout(() => {
    				timer = null
    			}, time)
    		}
    	}
    }
    // 实现五百毫秒才能调用一次
    
  • Debounce
    function debounce(fn,dur){
    	dur = dur || 100;
    	var timer;
    	return function() {
    		clearTimeout(timer);
    		timer = setTimeout(() => {
    			fn.apply(this, arguments):
    		}, dur);
    	}
    }		
    
  • Consumer / 2
  • 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]);
    	}
    }
    

编程范式

命令式与声明式
命令式与声明式.png
命令式:怎么做
声明式:做什么

总结

  • 过程抽象 / HOF / 装饰器
  • 命令式 / 声明式

质量优化

  • 根据实际情况来评估代码
  • 用算法思维来优化代码

总结

今天的内容是如何写好JavaScript,主要介绍了前端项目中需要注意的三个原则——各司其责、组件封装、过程抽象等三个原则和如何提高代码的质量。