欢迎光临
我们一直在努力

前端也需 OOP 思维!面向过程 vs 面向对象开发,90% 的人没搞懂

前端也需 OOP 思维!面向过程 vs 面向对象开发,90% 的人没搞懂

今天遇到个挺有代表性的事:我吭哧吭哧写完一个需求,领导 review 代码时说:“你这是面向过程开发的,得用面向对象的思维来写。”

我当时就懵了——前端 JS/TS 里,面向过程和面向对象到底有啥区别?不都是写函数、调 API 吗?

直到我把两段代码摆在一起对比,才恍然大悟。


一个真实场景:用户订单处理

假设我们要处理用户订单,计算价格、验证库存、生成记录。

❌ 面向过程写法(我最初写的)

// 一堆函数,数据到处传递
function calculateTotal(price: number, quantity: number, discount: number): number {
return price * quantity * (1 – discount);
}

function checkStock(productId: string, quantity: number): boolean {
// 查询数据库或API
const stock = getStockFromDB(productId);
return stock >= quantity;
}

function createOrder(userId: string, productId: string, quantity: number): Order {
const price = getPrice(productId);
const discount = getUserDiscount(userId);

if (!checkStock(productId, quantity)) {
throw new Error('库存不足');
}

const total = calculateTotal(price, quantity, discount);

return {
id: generateId(),
userId,
productId,
quantity,
total,
createdAt: new Date()
};
}

// 数据像皮球一样在函数间传来传去
const order = createOrder('user123', 'product456', 2);

面向过程的特点:

  • 数据和行为分离:数据(price、quantity)和操作它们的函数是分开的
  • 函数是主角:一切围绕函数展开
  • 数据公开透明:谁都可以修改数据
  • 顺序执行:像食谱一样,第一步、第二步…
  • ✅ 面向对象写法(领导想要的)

    class Order {
    private id: string;
    private userId: string;
    private productId: string;
    private quantity: number;
    private total: number;
    private createdAt: Date;

    constructor(userId: string, productId: string, quantity: number) {
    this.id = generateId();
    this.userId = userId;
    this.productId = productId;
    this.quantity = quantity;
    this.createdAt = new Date();

    this.validate();
    this.calculateTotal();
    }

    private validate(): void {
    if (!this.checkStock()) {
    throw new Error('库存不足');
    }
    if (this.quantity <= 0) {
    throw new Error('数量必须大于0');
    }
    }

    private checkStock(): boolean {
    const stock = StockService.getStock(this.productId);
    return stock >= this.quantity;
    }

    private calculateTotal(): void {
    const price = ProductService.getPrice(this.productId);
    const discount = UserService.getDiscount(this.userId);
    this.total = price * this.quantity * (1 – discount);
    }

    public applyCoupon(couponCode: string): void {
    const discount = CouponService.validate(couponCode);
    this.total *= (1 – discount);
    }

    public getSummary(): OrderSummary {
    return {
    id: this.id,
    productId: this.productId,
    quantity: this.quantity,
    total: this.total,
    createdAt: this.createdAt
    };
    }
    }

    // 使用:创建一个订单对象,一切都在对象内部处理
    const order = new Order('user123', 'product456', 2);
    order.applyCoupon('SAVE10');
    const summary = order.getSummary();

    面向对象的特点:

  • 数据和行为封装在一起:订单数据和处理订单的方法在一个类里
  • 对象是主角:一切围绕对象展开
  • 数据私有化:外部不能直接修改内部数据
  • 对象自己负责自己:订单对象知道如何验证自己、计算自己的价格

  • 核心区别:思维模式不同

    面向过程:做事

    “我要处理订单,先查价格,再查库存,然后计算总价…”

    像在餐馆点餐:告诉厨师每一步要做什么。

    面向对象:找对象

    “我需要一个订单对象,它能自己验证、自己计算价格…”

    像在餐馆吃饭:告诉服务员“来份牛排”,牛排怎么做是厨房的事。


    4个具体对比,一看就懂

    对比1:数据管理

    // 面向过程:数据是公开的
    let user = { name: '张三', age: 25 };
    function updateUserName(user, newName) {
    user.name = newName; // 直接修改
    }

    // 面向对象:数据是私有的
    class User {
    private name: string;
    private age: number;

    public setName(newName: string): void {
    if (newName.length < 2) {
    throw new Error('姓名太短');
    }
    this.name = newName; // 通过方法修改,可以加验证
    }
    }

    对比2:错误处理

    // 面向过程:错误检查分散在各处
    function processOrder(order) {
    if (!order.user) return '用户不存在';
    if (!order.product) return '商品不存在';
    if (order.quantity <= 0) return '数量错误';
    // …
    }

    // 面向对象:错误检查在对象创建时
    class Order {
    constructor(user, product, quantity) {
    if (!user) throw new Error('用户不存在');
    if (!product) throw new Error('商品不存在');
    if (quantity <= 0) throw new Error('数量错误');
    // 一次性检查完
    }
    }

    对比3:功能扩展

    // 面向过程:加功能就要改很多函数
    function calculateTotal(price, quantity, discount) {
    // 突然要加会员折扣
    // 得修改这个函数,加参数、改逻辑
    }

    // 面向对象:加功能只需扩展类
    class Order {
    calculateTotal() {
    let total = this.price * this.quantity;
    total *= (1 – this.discount);

    // 加会员折扣很简单
    if (this.user.isVIP) {
    total *= 0.9;
    }
    }
    }

    对比4:代码组织

    // 面向过程:按步骤组织代码
    // order.js
    function validateOrder() { /*…*/ }
    function calculatePrice() { /*…*/ }
    function checkStock() { /*…*/ }
    function createInvoice() { /*…*/ }

    // 面向对象:按领域组织代码
    // Order.js
    class Order { /* 订单相关的一切 */ }

    // Product.js
    class Product { /* 商品相关的一切 */ }

    // User.js
    class User { /* 用户相关的一切 */ }


    什么时候用哪种?别搞错

    用面向过程 ✅

  • 简单脚本:一次性的数据处理脚本
  • 工具函数:utils/helpers 里的纯函数
  • 性能关键:需要极致性能的场景
  • 函数式编程:数据不可变,函数是第一公民
  • // 工具函数适合面向过程
    export function formatDate(date: Date): string {
    return `${date.getFullYear()}–${date.getMonth() + 1}–${date.getDate()}`;
    }

    export function debounce(fn: Function, delay: number): Function {
    let timer: NodeJS.Timeout;
    return (…args: any[]) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(…args), delay);
    };
    }

    用面向对象 ✅

  • 复杂业务逻辑:电商、CRM、ERP 系统
  • 状态丰富的对象:用户、订单、商品
  • 需要封装和隐藏细节:支付、权限等敏感操作
  • 容易扩展的系统:需要经常加新功能
  • // 电商系统适合面向对象
    class ShoppingCart {
    private items: CartItem[] = [];
    private userId: string;

    public addItem(product: Product, quantity: number): void {
    // 验证、计算、更新一气呵成
    }

    public checkout(): Order {
    // 复杂的结账逻辑封装在里面
    }

    public getRecommendations(): Product[] {
    // 基于用户历史的推荐
    }
    }


    前端特有的考量

    在前端,我们还要考虑这些:

    React 组件:其实是面向对象的

    // React 组件本质上是对象(有状态、生命周期)
    class UserProfile extends React.Component {
    constructor(props) {
    super(props); // 初始化
    this.state = { user: null }; // 封装状态
    }

    componentDidMount() {
    this.loadUser(); // 行为
    }

    loadUser = async () => {
    const user = await api.getUser(this.props.userId);
    this.setState({ user }); // 修改状态
    }

    render() {
    return this.state.user ? (
    <div>{this.state.user.name}</div>
    ) : null;
    }
    }

    Vue 3 Composition API:两者结合

    // 组合式函数(像面向过程)
    function useUser(userId: string) {
    const user = ref<User | null>(null);

    async function loadUser() {
    user.value = await api.getUser(userId);
    }

    return { user, loadUser };
    }

    // 但在组件中使用时(像面向对象)
    export default defineComponent({
    setup(props) {
    // 逻辑组合在一起
    const { user, loadUser } = useUser(props.userId);
    const { orders, loadOrders } = useOrders(props.userId);

    return { user, orders, loadUser, loadOrders };
    }
    });


    如何从面向过程转到面向对象?

    如果你习惯了面向过程,可以这样过渡:

    第1步:识别“名词”

    看你的代码,找出主要的名词(用户、订单、商品),这些就是潜在的类。

    第2步:封装数据

    把散落在各处的相关数据,包装到一个类里。

    第3步:归并行为

    把操作这些数据的函数,变成类的方法。

    第4步:设定边界

    决定哪些公开(public),哪些私有(private)。

    重构示例:

    // 之前:数据和行为分散
    let cartItems = [];
    function addToCart(item) { /*…*/ }
    function calculateTotal() { /*…*/ }
    function applyDiscount() { /*…*/ }

    // 之后:封装成类
    class ShoppingCart {
    private items: CartItem[] = [];

    public addItem(item: CartItem): void { /*…*/ }
    private calculateTotal(): number { /*…*/ }
    public applyDiscount(code: string): void { /*…*/ }
    }


    领导到底在说什么?

    当领导说“要用面向对象思维”时,他其实在说:

  • 封装性:“别让外部随便改数据”
  • 职责清晰:“一个类只做一件事”
  • 易扩展:“加功能别影响老代码”
  • 易维护:“出了问题知道去哪儿找”
  • 这不是在批评你代码写得不好,而是在教你写出更容易维护、更容易扩展的代码。


    最后的大实话

    在前端开发中,没必要所有代码都面向对象。好的做法是:

    • 业务核心逻辑用面向对象(封装、易维护)
    • 工具函数/UI逻辑用面向过程(简单、直接)
    • 状态管理用函数式(可预测、易测试)

    就像做菜一样,面向过程是菜谱(先放油,再放菜),面向对象是预制菜(加热就能吃)。

    关键是根据场景选对方法。今天你知道区别了,下次领导再提,你不仅能改,还能跟他讨论哪种更适合。

    记住:最好的代码不是“最面向对象”的代码,而是最适合当前需求的代码。

    赞(0)
    未经允许不得转载:171主机测评 » 前端也需 OOP 思维!面向过程 vs 面向对象开发,90% 的人没搞懂
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址