You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何用class而非function创建对象?两种方式的差异与选型

构造函数 vs ES6 Class:功能差异与选择指南

我曾简要学习一款2D游戏教程,其中展示了如下对象创建方式:

function component(width, height, color, x, y) {
  this.width = width ...
  this.method = function(){...}
}
// 使用构造函数实例化block
block = new component(30, 60, "red", 225, 225);

同时我也了解到ES6的class特性,其实现逻辑类似,但更贴合其他语言的正式对象创建语法:

class component {
  constructor(width, height, color, x, y){
    this.width = width ...
  }
  method(){ ... }
}
// 使用class实例化block
block = new component(30, 60, "red", 225, 225);

请问这两种实现方式存在哪些功能差异?我应如何选择使用哪一种?

嘿,这个问题问得很到位——很多刚接触JS面向对象的开发者都会纠结这两种写法的区别,我来给你掰扯清楚:

一、核心功能差异

1. 方法的存储逻辑天差地别

  • 构造函数版本:你写在构造函数里的method,每次new实例都会重新创建一次。就像你做游戏里的方块,每个红方块、蓝方块都带了一份独立的方法副本,内存开销会随着实例增多变大。
    举个例子:
    const block1 = new component(30,60,"red",225,225);
    const block2 = new component(30,60,"blue",225,225);
    console.log(block1.method === block2.method); // false,俩完全不同的函数
    
  • ES6 Class版本:Class里的方法默认挂在类的prototype原型上,所有实例共享同一份方法。不管你创建多少个方块,大家用的都是同一个method,内存效率高很多:
    const block1 = new component(30,60,"red",225,225);
    const block2 = new component(30,60,"blue",225,225);
    console.log(block1.method === block2.method); // true,共享原型上的方法
    
    (顺便说一句:如果想让构造函数也实现共享方法,得手动把方法挂在component.prototype上,但新手很容易忘这一步)

2. 语法可读性与维护成本

  • 构造函数是ES5及之前的原生写法,语法比较零散,比如要加共享方法、处理继承都得手动折腾原型链,容易写错,团队协作时新人上手慢。
  • Class是ES6的语法糖(本质还是基于原型链,但包装得更友好),写法和Java、Python这些传统面向对象语言的类几乎一致,结构清晰,一眼就能看懂哪个是构造函数、哪个是实例方法、哪个是静态方法,维护起来省心太多。

3. 继承实现的复杂度

  • 构造函数搞继承:步骤繁琐到让人头疼——得先调用父构造函数,再手动继承原型,还要修正构造函数指向,稍不留神就出bug:
    function subComponent(width, height, color, x, y, speed) {
      component.call(this, width, height, color, x, y); // 手动调用父构造函数
      this.speed = speed;
    }
    subComponent.prototype = Object.create(component.prototype); // 继承父类原型
    subComponent.prototype.constructor = subComponent; // 修正构造函数指向,不然会指向component
    
  • Class搞继承:用extends和super关键字一键搞定,语法简洁到离谱,完全不用管原型链的细节:
    class subComponent extends component {
      constructor(width, height, color, x, y, speed) {
        super(width, height, color, x, y); // 调用父类构造函数
        this.speed = speed;
      }
    }
    

4. 静态成员的定义方式

  • 构造函数定义静态方法/属性:只能直接挂载在构造函数本身上,语法没那么规范:
    component.staticMethod = function() { console.log("我是静态方法"); };
    component.staticProp = "游戏配置";
    
  • Class定义静态成员:用static关键字明确定义,一眼就能区分静态和实例成员,代码更规整:
    class component {
      static staticProp = "游戏配置";
      static staticMethod() { console.log("我是静态方法"); }
      // ... 其他实例内容
    }
    

5. 私有成员的支持

  • 构造函数:早期只能靠闭包模拟私有成员(但写法麻烦),或者用下划线命名约定(比如_privateProp),但本质还是能被外部访问;后来ES2022支持了私有字段,但构造函数写法远不如Class直观。
  • Class:ES2022开始支持用#定义真正的私有字段和方法,外部完全访问不到,语法明确,实现真正的封装:
    class component {
      #privateProp = "方块的秘密属性";
      #privateMethod() { /* 只有类内部能调用的方法 */ }
      // ...
    }
    

二、该怎么选?

  • 优先选Class(只要环境兼容ES6+):现在绝大多数项目都不用考虑IE这类老旧浏览器了,Class的可读性、维护性优势太明显。尤其是你做2D游戏,后续肯定要扩展玩家、敌人、道具这些不同组件,用Class做继承会顺畅很多,团队协作也不容易出问题。
  • 必须兼容老旧环境?用构造函数:如果你的游戏要支持IE11及以下,那只能用构造函数写法,或者用Babel把Class转成ES5代码。
  • 简单场景?随意但推荐Class:哪怕只是创建几个简单的方块对象,Class的写法也更清晰,不容易出错。

内容的提问来源于stack exchange,提问作者goose

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:53:05