写好JavaScript的三个原则:各司其责 组件封装 过程抽象
各司其责
- HTML/CSS/JavaScript各司其责
- HTML -> Structural ; CSS -> Presentational ; JavaScript -> Behavioral
- 应当避免不必要的由JS直接操作样式
- 可以用class来表示状态
- 纯展示类交互应寻求零JS方案
组件封装
组件是指Web页面上抽出来的一个个包含模块(HTML)、样式(CSS)和功能(JS)的单元。好的组件具备封装性、正确性、扩展性、复用性。实现组件的步骤:结构设计、展现效果、行为设计,三次重构:插件化重构、模板化重构、抽象化重构。
- 结构设计:HTML
- 展现效果:CSS
- 行为设计:JS
- API(功能),API 设计应保证原子操作,职责单一,满足灵活性。
- Event(控制流),使用自定义事件来解耦。
- 插件化重构,即解耦
- 将控制元素抽取成插件
- 插件与组件之间通过依赖注入方式建立联系
- 模板化重构
- 将HTML模板化,更易于扩展
- 抽象化重构(组件框架)
- 将通用的组件模型抽象出来
过程抽象
过程抽象是指用来处理局部细节控制的一些方法,是函数式编程思想的基础应用。
- 高阶函数
- 以函数作为参数
- 以函数作为返回值
- 常用于函数装饰器
//零阶高阶函数,等价于直接调用函数
function HOF_0 (fn){
return function(...args){
return fn.apply(this.args);
}
}
- 构造 once 高阶函数,为了能够让“只执行一次”的需求(例如一些异步操作、一次性的HTTP请求)覆盖不同的事件处理,我们可以将这个需求利用闭包剥离出来。这个过程称为过程抽象。
function once ( fn ) {
return function (...args){
const res = fn.allpy (this.args);
fn = null;
return res;
}
}
- 防抖函数,在第一次触发事件时,不立即执行函数,而是给出一个期限值,如果在期限值内没有再次触发滚动事件,那么就执行函数,如果在期限值内再次触发滚动事件,那么当前的计时取消,重新开始计时
const debounce = (fn, delay) => {
let timer = null;
//借助闭包
return function () {
clearTimeout(timer);
timer = setTimeout(fn, delay);
}
}
- 节流函数,类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活。效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
const myThrottle = (fn, delay) => {
let flag = true;
return function () {
if (!flag) {
return false;
} else {
flag = false;
setTimeout(() => {
fn();
flag = true;
}, delay);
}
}
}
- 为什么要使用高阶函数?
- 减少系统内非纯函数的数量,提高系统的可测试性和稳定性。
写好 JavaScript
写代码应该关注
- 效率
- 风格
- 使用场景
- 约定
- 设计
具体的代码实现要因场景而定,不同的场景注重点不一样,例如在某些比较底层的场景,可能更注重效率,而在多人协作的时候可能更关注约定。
判断是否是4的幂
- 常规操作
const isPowerOfFour = (num) => {
num = parseInt(num);
while(num > 1){
if(num % 4) return false;
num /= 4;
}
return true;
}
- 优化版本1,利用 4 的幂的二进制数最高位为1,低位为偶数个0
const isPowerOfFour = (num) => {
num = parseInt(num);
return num > 0 &&
(num & (num-1)) === 0 &&
(num & 0XAAAAAAAA) === 0;
}
- 优化版本2,利用正则表达式
const isPowerOfFour = (num) => {
num = parseInt(num).toString(2);
return /^1(?:00)*$/.test(num);
}
实现交通灯切换效果
- 版本一,利用setTimeout,可能出现回调地狱
const traffic = document.getElementById('traffic');
(function reset(){
traffic.className = 's1';
setTimeout(function(){
traffic.className = 's2';
setTimeout(function(){
traffic.className = 's3';
setTimeout(function(){
traffic.className = 's4';
setTimeout(function (){
traffic.className = 's5';
setTimeout(reset,1000)
},1000)
},1000)
},1000)
},1000)
})();
- 优化版本,利用async/await
const traffic = document.getElementById('traffic');
function wait(time){
return new Promise(resolve => setTimeout(resolve,time))
}
function setState(state){
traffic.className = state;
}
async function start(){
while(1){
setState('wait');
await wait(1000);
setState('stop');
await wait(3000);
setState('pass');
await wait(3000);
}
}
start();
洗牌算法
- 错误示例
看似可以正确洗牌,但实际上较小的牌放到前面的概率更大,较大的的牌放到后面的概率更大
const cards = [0,1,2,3,4,5,6,7,8,9];
const shuffle = (cards) => {
return [...cards].sort(() => { Math.random() > 0.5 ? -1 : 1 });
}
- 正确示例
const cards = [0,1,2,3,4,5,6,7,8,9];
function *draw(cards){
const c = [...cards];
for(let i = c.length ; i > 0 ; i--){
const pIdx = Math.floor(Math.random() * i);
[c[pIdx],c[i - 1]] = [c[i - 1],c[pIdx]];
yield c[i - 1];
}
return c;
}
分红包问题
- 版本一,类似于切蛋糕,取最大的继续切分,得出的结果会比较平均
function generate(amount,count){
let ret = [amount];
while(count > 1){
// 挑出最大的进行切分
let cake = Math.max(...ret),
idx = ret.indexOf(cake),
part = 1 + Math.floor((cake / 2) * Math.random()),
rest = cake - part;
ret.splice(idx,1,part,rest);
count --;
}
return ret;
}
- 版本二,随机性较大,可能会有较大的红包出现
function *draw(cards){
const c = [...cards];
for(let i = c.length ; i > 0 ; i --){
const pIdx = Math.floor(Math.random()*i);
[c[pIdx],c[i-1]] = [c[i - 1],c[pIdx]];
yield c[i-1]
}
return c;
}
function generate(amount,count){
if(count <= 1) return [amount];
const cards = Array(amount - 1).fill(null).map((_,i) => i+1);
const pick = draw(cards);
const result = [];
for(let i = 0 ; i < count; i++){
result.push(pick.next().value);
}
result.sort((a,b)=>a-b);
for(let i = count - 1 ; i > 0 ; i--){
result[i] = result[i] - result[i - 1];
}
return result;
}
本文介绍了JavaScript编程的三个核心原则:各司其责、组件封装与过程抽象。文章深入探讨了如何通过合理分配HTML、CSS及JavaScript的角色来构建清晰的网页结构,并通过组件化的思想提升代码的复用性和维护性。
1281

被折叠的 条评论
为什么被折叠?



