JS编码原则和一些算法

81 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

JS编码原则

写好JS的一些原则

各司其职:让HTML、CSS和JavaScript职能分离

组件封装:好的UI组件具备正确性、扩展性、复用性

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

例子:切换深夜食堂的日间夜间模式

应当避免不必要的由JS直接操作样式;可以用class来表示状态;纯展示类建议寻求零JS方案

例子:用原生JS写一个电商网站的轮播图

结构:轮播图是一个典型的列表结构,可以用无序列表ul元素来实现

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

行为:API

Slider

+getSelectedltem()

+getSelectedltemIndex()

+slideTo()

+slideNext()

+slidePrevious()

行为:控制流

使用自定义事件来解耦

总结:基本方法

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

改进

重构:插件化

解耦 将控制元素抽取成插件;插件与组件之间通过依赖注入方式建立联系

重构:模板化

解耦

将HTML模板化,更易于扩展

组件框架

抽象

将组件通用模型抽象出来

总结:

组件设计的原则:封装性、正确性、扩展性、复用性

实现组件的步骤:结构设计、展现效果、行为设计

三次重构:插件化;模板化;抽象化(组件框架)

过程抽象

用来处理局部细节控制的一些方法;函数式变成思想的基础应用

once

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

HOF高阶函数

以函数作为参数;以函数作为返回值;常用于作为函数装饰器

Throttle

是一种节流技术,通常用于限制事件的触发频率。它的作用是在一段时间内只允许事件触发一次,通过限制事件的触发频率来减少不必要的处理和负载。

Debounce

是一种防抖技术,通常用于在一段时间内只响应最后一次事件。它的作用是延迟事件的触发,在事件触发后的一段时间内,如果再次触发了该事件,则取消之前的事件。

Consumer/2

是一种特殊类型的消费者函数。 它接受两个参数: 一个函数(称为“消费函数”)和一个值,并调用消费函数,将该值作为参数传递给它。

Iterative

是一种迭代的技术,通常指重复执行一段代码的过程。迭代可以用循环语句来实现,如 for 循环和 while 循环。在编程中迭代是一种常用的技巧,用于处理重复性工作,如遍历数组、字符串等。

为什么要使用高阶函数

灵活性: 高阶函数允许我们将函数作为参数或返回值,这样我们就可以在运行时动态地更改函数的行为。

可重用性: 高阶函数允许我们抽象出通用的模式,并将其封装在一个函数中,这样我们就可以重用这个函数来完成相似的任务。

组合性: 高阶函数允许我们将小函数组合成大函数,这样我们就可以使用简单的函数来构建复杂的程序。

可读性: 高阶函数允许我们将复杂的逻辑分解成多个小函数,这样我们就可以更容易地理解和维护代码。

编程范式

命令式与声明式

例子

Toggle-命令式

更直接,简洁

Toggle-声明式

可扩展性更强,在状态比较多的时候修改够更便捷

Toggle-三态

改进这段代码

检查这个矩阵是不是单位矩阵

function isUnit(){ 

return m[0] ===1 && m[1]===0 && m[2]===0 && m[3]===1 && m[4]===0 && m[5]===0;

}

使用数组的 every() 方法来检查所有元素是否都符合特定条件。这可以使代码更简洁和易读。

function isUnit(m) {    
    const expectedValues = [1, 0, 0, 1, 0, 0];
    return m.every((val, idx) => val === expectedValues[idx]);
    }

使用常量来存储期望值。这可以使代码更易读,并且如果需要修改期望值,只需要修改一处即可。


const UNIT_MATRIX = [1, 0, 0, 1, 0, 0];

function isUnit(m) {

    for(var i=0;i<m.length;i++){
        if(m[i]!== UNIT_MATRIX[i]) return false;
    }
    return true;
}

使用一个变量来存储单位矩阵,这样可以使比较更加简洁,例如

const UNIT_MATRIX = [1, 0, 0, 1, 0, 0];
function isUnit(m) {
    return JSON.stringify(m) === JSON.stringify(UNIT_MATRIX);
}

写代码最应该关注什么

风格、效率、约定、使用场景、设计

leftpad

实现一个切换多个交通灯状态切换的功能

版本一:命令式 版本二:数据抽象 版本三:过程抽象 版本四:异步+函数式

判断是否是4的幂(力扣)

洗牌算法

random是伪随机数,在大量数据面前会出现误差 使用随机数生成器就可以避免这个情况

const crypto = require('crypto');

function shuffle(arr) {
    for (let i = arr.length - 1; i > 0; i--) {
        let j = crypto.randomBytes(1)[0] % (i + 1);
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
}

分红包-切西瓜算法

function cutWatermelon(totalAmount, numOfPeople) {
    // 红包金额最低限制
    const minAmount = 0.01;
    // 创建一个空数组来存储每个人分到的金额
    const amounts = [];
    // 记录剩余金额
    let remainingAmount = totalAmount;
    // 验证输入的红包金额和红包个数
    if (numOfPeople < 1 || totalAmount < numOfPeople * minAmount) {
        return 'Invalid input: please check the number of people or total amount.';
    }
    // 循环分配金额
    while(true){
    for (let i = 0; i < numOfPeople - 1; i++) {
        // 生成一个随机数,作为当前人分到的金额
        let currentAmount = (Math.random() * Math.min(remainingAmount - minAmount, (remainingAmount - minAmount * (numOfPeople-i))) + minAmount).toFixed(2);
        // 将当前人分到的金额加入数组
        amounts.push(currentAmount);
        // 减去分到的金额
        remainingAmount -= currentAmount;
    }
    // 剩余金额分配给最后一个人
    amounts.push((remainingAmount).toFixed(2));
    //如果最后一人获得的红包小于0.01,那么重新进行循环覆盖之前的数