50projects50days JavaScript模式:函数式与面向对象编程

50projects50days JavaScript模式:函数式与面向对象编程

【免费下载链接】50projects50days 50+ mini web projects using HTML, CSS & JS 【免费下载链接】50projects50days 项目地址: https://gitcode.com/GitHub_Trending/50/50projects50days

你是否在开发Web应用时纠结于选择函数式编程(Functional Programming, FP)还是面向对象编程(Object-Oriented Programming, OOP)?50projects50days项目通过50个迷你Web应用,展示了JavaScript中两种范式的实战应用。本文将深入分析这些项目中的代码模式,帮你掌握何时选择函数式、何时选择面向对象,以及如何混合使用两种范式解决实际问题。

读完本文你将获得:

  • 识别JS项目中函数式与面向对象模式的能力
  • 3类典型场景的范式选择决策指南
  • 从50个实战项目提炼的8个代码模板
  • 混合编程模式的最佳实践

范式对比:核心差异与项目应用

本质区别

特性函数式编程面向对象编程
核心单元纯函数类与对象
状态管理不可变数据封装的可变状态
代码组织函数组合继承与多态
适用场景数据处理、管道操作复杂实体、UI组件
50projects占比约65%约35%

项目中的范式分布

mermaid

函数式编程:项目实战解析

函数式编程在50projects中占比最高,尤其适合状态独立、逻辑复用的场景。其核心特点是:纯函数、不可变数据、函数组合和声明式代码。

1. 纯函数与数据转换

密码生成器项目展示了纯函数的典型应用,通过独立函数实现不同类型字符的生成:

// 纯函数:无副作用,相同输入始终返回相同输出
function getRandomLower() {
    return String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}

function getRandomUpper() {
    return String.fromCharCode(Math.floor(Math.random() * 26) + 65);
}

function getRandomNumber() {
    return String.fromCharCode(Math.floor(Math.random() * 10) + 48);
}

这些函数具有以下特点:

  • 无外部依赖(不读取/修改全局变量)
  • 输出仅由输入决定
  • 无副作用(不修改参数或外部状态)

2. 函数组合与高阶函数

随机选择器项目展示了函数组合的威力,通过将多个简单函数组合成复杂逻辑:

// 高阶函数:接收函数作为参数或返回函数
function createTags(input) {
    return input
        .split(',')
        .filter(tag => tag.trim() !== '')
        .map(tag => `<span class="tag">${tag.trim()}</span>`)
        .join('');
}

// 事件处理中的函数组合
textarea.addEventListener('keyup', (e) => {
    const tags = createTags(e.target.value);
    tagsEl.innerHTML = tags;
    
    if (e.key === 'Enter') {
        setTimeout(() => {
            e.target.value = '';
        }, 10);
        randomSelect();
    }
});

3. 不可变数据模式

在计数器项目中,通过替换而非修改实现状态更新:

// 不可变更新:创建新数组而非修改原数组
function updateCounter() {
    const target = +counter.getAttribute('data-target');
    const count = +counter.innerText;
    const increment = target / 200;

    if (count < target) {
        counter.innerText = Math.ceil(count + increment);
        setTimeout(updateCounter, 1);
    } else {
        counter.innerText = target;  // 最终赋值确保精确性
    }
}

// 批量处理多个计数器(函数式数据处理)
counters.forEach(counter => {
    updateCounter(counter);
});

面向对象编程:类与实例实战

虽然函数式编程占比更高,但面向对象编程在特定场景中表现更优,特别是需要维护内部状态和提供复杂行为的组件。

1. 类与封装

虽然50projects中直接使用class语法的项目较少,但Todo List应用展示了典型的面向对象思想:

// 隐式的对象封装模式
function addTodo(todo) {
    let todoText = input.value;
    
    if (todo) {
        todoText = todo.text;  // 从对象中获取数据
    }
    
    if (todoText) {
        const todoEl = document.createElement('li');
        if (todo && todo.completed) {
            todoEl.classList.add('completed');  // 维护完成状态
        }
        
        todoEl.innerText = todoText;
        
        // 封装行为:点击切换完成状态
        todoEl.addEventListener('click', () => {
            todoEl.classList.toggle('completed');
            updateLS();  // 状态变更持久化
        });
        
        // 右键删除功能
        todoEl.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            todoEl.remove();
            updateLS();
        });
        
        todosUL.appendChild(todoEl);
        input.value = '';
        updateLS();
    }
}

这个实现虽然没有使用class关键字,但体现了OOP的核心思想:

  • 数据(todo.text, completed状态)和行为(toggle, remove)的封装
  • 通过方法(updateLS)维护内部状态
  • 对象实例(每个todoEl)独立管理自己的状态

2. 原型与继承

测验应用展示了如何通过原型模式实现代码复用:

// 测验数据结构(可视为类的原型定义)
const quizData = [
    {
        question: "Which language runs in a web browser?",
        a: "Java",
        b: "C",
        c: "Python",
        d: "JavaScript",
        correct: "d",
    },
    // 更多问题...
];

// 测验状态管理(类似类实例的状态)
let currentQuiz = 0;
let score = 0;

// 方法定义(类似类的方法)
function loadQuiz() {
    deselectAnswers();
    const currentQuizData = quizData[currentQuiz];
    questionEl.innerText = currentQuizData.question;
    a_text.innerText = currentQuizData.a;
    // 设置其他选项...
}

function getSelected() {
    let answer;
    answerEls.forEach(answerEl => {
        if(answerEl.checked) {
            answer = answerEl.id;
        }
    });
    return answer;
}

混合编程:最佳实践与项目案例

大多数复杂项目需要混合使用两种范式。50projects50days中的最佳实践是:用函数式处理数据,用面向对象管理状态

1. 面向对象封装 + 函数式数据处理

密码生成器项目完美展示了这种混合模式:

// 函数式部分:纯函数与函数组合
const randomFunc = {
    lower: getRandomLower,
    upper: getRandomUpper,
    number: getRandomNumber,
    symbol: getRandomSymbol
};

function generatePassword(lower, upper, number, symbol, length) {
    // 函数组合:过滤->映射->归约
    let generatedPassword = '';
    const typesCount = lower + upper + number + symbol;
    const typesArr = [{lower}, {upper}, {number}, {symbol}]
        .filter(item => Object.values(item)[0]);
    
    // 函数式循环:避免直接修改变量
    for(let i = 0; i < length; i += typesCount) {
        typesArr.forEach(type => {
            const funcName = Object.keys(type)[0];
            generatedPassword += randomFunc[funcName]();
        });
    }
    
    return generatedPassword.slice(0, length);
}

// 面向对象部分:事件监听与状态管理
generateEl.addEventListener('click', () => {
    const length = +lengthEl.value;
    const hasLower = lowercaseEl.checked;
    const hasUpper = uppercaseEl.checked;
    const hasNumber = numbersEl.checked;
    const hasSymbol = symbolsEl.checked;
    
    resultEl.innerText = generatePassword(
        hasLower, hasUpper, hasNumber, hasSymbol, length
    );
});

2. 函数式管道 + 类封装

复杂表单处理可以采用这种模式:

mermaid

场景决策指南:何时选择哪种范式

选择函数式编程当:

  1. 数据转换管道:如密码生成、数据过滤和格式化

    // 示例:数据处理管道
    function processUserData(users) {
        return users
            .filter(user => user.active)
            .map(user => ({
                id: user.id,
                fullName: `${user.firstName} ${user.lastName}`,
                age: calculateAge(user.birthdate)
            }))
            .sort((a, b) => a.age - b.age);
    }
    
  2. 无状态工具函数:如DOM操作、数学计算

    // 示例:纯工具函数
    const scale = (num, in_min, in_max, out_min, out_max) => {
        return (num - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    };
    
  3. 事件处理逻辑:如按钮点击、表单提交

    // 示例:事件处理函数组合
    next.addEventListener('click', () => {
        if (currentActive >= circles.length) return;
        currentActive++;
        update();
    });
    

选择面向对象编程当:

  1. 复杂状态管理:如测验进度、游戏状态

    // 伪代码:游戏状态管理
    class Game {
        constructor() {
            this.score = 0;
            this.level = 1;
            this.enemies = [];
            this.player = { x: 0, y: 0, health: 100 };
        }
    
        start() { /* ... */ }
        update() { /* ... */ }
        render() { /* ... */ }
        gameOver() { /* ... */ }
    }
    
  2. UI组件封装:如自定义表单控件、交互组件

    // 伪代码:自定义组件
    class Dropdown {
        constructor(element) {
            this.element = element;
            this.items = element.querySelectorAll('.dropdown-item');
            this.isOpen = false;
            this.bindEvents();
        }
    
        bindEvents() {
            this.element.addEventListener('click', () => this.toggle());
        }
    
        toggle() {
            this.isOpen = !this.isOpen;
            this.element.classList.toggle('open', this.isOpen);
        }
    }
    
  3. 可复用实体:如待办事项、用户账户

    // 伪代码:实体类
    class TodoItem {
        constructor(text) {
            this.id = Date.now();
            this.text = text;
            this.completed = false;
            this.createdAt = new Date();
        }
    
        toggleComplete() {
            this.completed = !this.completed;
            this.updatedAt = new Date();
        }
    
        toJSON() {
            return {
                id: this.id,
                text: this.text,
                completed: this.completed,
                createdAt: this.createdAt,
                updatedAt: this.updatedAt
            };
        }
    }
    

常见问题与解决方案

Q1: 如何在函数式项目中处理状态?

A: 使用不可变更新模式和状态容器:

// 不可变状态更新
function updateState(oldState, newData) {
    return {
        ...oldState,
        ...newData,
        // 嵌套对象也需要复制
        user: {
            ...oldState.user,
            ...newData.user
        }
    };
}

// 状态管理容器
const appState = {
    theme: 'light',
    user: { name: '', preferences: {} },
    todos: []
};

// 状态更新函数
function setTheme(theme) {
    appState = updateState(appState, { theme });
    renderUI(appState);
}

Q2: 如何避免OOP中的继承层级过深?

A: 使用组合优于继承原则:

// 继承方式(不推荐)
class SpecialButton extends Button {
    constructor() {
        super();
        this.hasIcon = true;
        this.hasDropdown = true;
    }
    
    // ...大量方法
}

// 组合方式(推荐)
class Button {
    constructor(options = {}) {
        this.elements = {};
        this.components = [];
        
        if (options.hasIcon) {
            this.components.push(new IconComponent());
        }
        
        if (options.hasDropdown) {
            this.components.push(new DropdownComponent());
        }
    }
    
    render() {
        const button = document.createElement('button');
        this.components.forEach(component => {
            button.appendChild(component.render());
        });
        return button;
    }
}

Q3: 混合编程时如何保持代码清晰?

A: 遵循明确的边界划分:

// 清晰分离数据处理(函数式)和状态管理(OOP)
class UserManager {
    constructor() {
        this.users = [];
        this.loadUsers();
    }
    
    // OOP: 状态管理方法
    loadUsers() {
        const data = localStorage.getItem('users');
        this.users = data ? JSON.parse(data) : [];
    }
    
    saveUsers() {
        localStorage.setItem('users', JSON.stringify(this.users));
    }
    
    // 混合: 使用函数式处理数据
    getActiveUsers() {
        return filterActiveUsers(this.users);
    }
    
    searchUsers(query) {
        return searchUsers(this.users, query);
    }
}

// 纯函数: 数据处理(函数式)
function filterActiveUsers(users) {
    return users.filter(user => user.active && !user.deleted);
}

function searchUsers(users, query) {
    const lowerQuery = query.toLowerCase();
    return users.filter(user => 
        user.name.toLowerCase().includes(lowerQuery) ||
        user.email.toLowerCase().includes(lowerQuery)
    );
}

总结与下一步

50projects50days项目展示了现代JavaScript开发的真实面貌:不再是纯粹的函数式或面向对象,而是根据问题选择合适的范式。优秀的JS开发者应当:

  1. 掌握两种范式的核心原则:纯函数、不可变性、封装和多态
  2. 根据场景灵活选择:数据处理用函数式,复杂实体用面向对象
  3. 混合编程时保持清晰边界:数据层与状态管理层分离

进阶学习路线

  1. 函数式进阶:学习Ramda或Lodash库,掌握curry、compose等高级概念
  2. 面向对象深入:学习TypeScript类、接口和设计模式
  3. 状态管理:研究Redux(函数式)和Vuex(OOP)的实现原理

实用工具推荐

  • 函数式编程:Ramda, Lodash/fp
  • 不可变数据:Immer, Immutable.js
  • 类型检查:TypeScript(提升两种范式的代码质量)

【免费下载链接】50projects50days 50+ mini web projects using HTML, CSS & JS 【免费下载链接】50projects50days 项目地址: https://gitcode.com/GitHub_Trending/50/50projects50days

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值