为何用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
相关产品推荐
相关产品推荐

