js Function构造函数
别再只背语法了:搞懂 JS 构造函数,才算摸到了面向对象的大门
很多初学者学完 ES6 的 class 关键字后,往往觉得 JavaScript 的对象系统终于变“像样”了。但如果你去维护十年前的老项目,或者看一些底层库源码时,会发现满天飞的还是 function 加 new 的组合。函数式构造函数虽然看起来古老,却藏着 JS 对象机制最底层的逻辑。不理解它,你看到的只是语法的糖衣,尝不到内核的味道。
想象一下,你的任务是要生成 100 个“用户”对象,每个都有姓名和年龄。如果手动写 100 遍 { name: '', age: '' },那绝对是在浪费生命。这时候就需要一个“工厂”,而构造函数就是这个工厂的核心模具。
function User(name, age) {
this.name = name;
this.age = age;
}
const u1 = new User('张三', 25);
这段代码看似简单,背后 new 运算符其实干了四件具体的事:创建一个新的空对象;将这个空对象的内部原型指向构造函数的 prototype;把函数体内的 this 绑定到新对象上并执行初始化代码;默认返回这个新对象。
理解这个过程,能帮你解决很多诡异的问题。比如,如果你忘记加 new 直接调用 User('李四'),this 就会指向全局对象(非严格模式下),导致数据污染。这种错误在大型项目中排查起来非常耗时。所以,规范编码习惯非常重要,使用大写字母开头作为构造函数标识是一种约定俗成的提醒方式。
说到效率,很多人会问:“为什么要把方法放原型上?”如果在构造函数里定义方法,比如 sayHi(),每次 new 出一个实例,内存里都会生成一份新的函数副本。当实例数量巨大时,这对内存是极大的浪费。
将公共方法挂载到 prototype 属性上,是构造函数时代的标准优化方案。所有实例共享同一份函数引用,访问链通过原型链向上查找。这不仅节省空间,还能让你在运行时动态扩展功能。你甚至不需要修改原来的构造函数定义,只要往 User.prototype 上挂新方法,所有已存在的实例也能立刻获得新功能。这点是后来 ES6 类语法继承下来保留的特性。
当然,构造函数并非完美无缺。最大的槽点在于继承不够直观,需要手动处理 call 或 apply 来借用构造函数,还要操心原型的链接。这也是为什么社区后来推出了 class。但你必须明白,class 本质上只是构造函数的一种语法糖。现代转译工具(如 Babel)在处理 class 时,生成的代码核心依然是基于构造函数和原型链实现的。
当你遇到打包后的压缩代码,变量名变成 a, b, c 分不清东西南北时,读懂构造函数原型链的思维模式能让你快速定位继承关系和数据来源。知道某个属性是来自实例自身,还是来自原型链上的共享,对于性能分析和 Bug 修复至关重要。
掌握构造函数,不是为了让你回去写旧代码,而是为了让你看清 JS 是如何“造”出对象的。这种对底层的掌控力,会让你在面对复杂的框架源码或设计模式时,少几分迷茫,多几分从容。毕竟,地基打稳了,往上盖什么风格的房子,都不再是问题。


还没有评论,来说两句吧...