JavaScript的秘密生活:蓝图
Source: Dev.to
ES6 类只是原型的“语法糖”
Timothy 站在黑板前,欣赏着自己的作品。他画了一个完美的矩形框。
“终于,”他拍了拍手上的灰尘说。“文明。”
在框里,他写下了一个类:
class Book {
constructor(title, author) {
this.title = title;
this.author = author;
}
read() {
console.log("Reading " + this.title);
}
}
const myBook = new Book("The Hobbit", "Tolkien");
“看看吧,Margaret,” Timothy 笑容满面地说。“它干净、结构化,看起来完全像 Java 或 C++ 中的蓝图。我们终于在库里拥有了真正的模板。”
Margaret 拿着放大镜和一把小螺丝刀走了过来。
“的确很漂亮,Timothy。但别被表面的涂层欺骗了。”
她用螺丝刀敲了敲 class 这个词。
“这不是模板,而是外衣。底层的机制和二十年前完全一样。”
Margaret 在黑板中间画了一条线。
“这就是你以为自己在写的东西,”她指着他的类说,“但这才是引擎真正看到的。”
在右侧,她写下了 旧写法——构造函数:
// 1. The Constructor Function
function Book(title, author) {
this.title = title;
this.author = author;
}
// 2. The Prototype Link
Book.prototype.read = function() {
console.log("Reading " + this.title);
};
const myBook = new Book("The Hobbit", "Tolkien");
“它们是完全相同的,” Margaret 透露道。“
class关键字不过是语法糖。它把构造函数和方法收拢到一个整齐的块里,但引擎会把它们拆开,构建同样的原型链。”
Timothy 问:“那就没有蓝图了吗?”
“没有,” Margaret 回答。“只有函数和相互链接的对象。”
new 实际上做了什么
Timothy 对 new 操作符产生了疑问。
“可是
new呢?这个词听起来很特别,像是启动了一台工厂机器。”
“它确实是一台机器,” Margaret 同意道。“一台非常简单的机器,只执行四个步骤。”
她擦掉代码,手写了一个手动实现来展示齿轮的转动:
// What 'new' actually does:
function fakeNew(Constructor, ...args) {
// Step 1: Create a new, empty object
const newObj = {};
// Step 2: Link it to the prototype
Object.setPrototypeOf(newObj, Constructor.prototype);
// Step 3: Bind 'this' and run the constructor
Constructor.apply(newObj, args);
// Step 4: Return the object
return newObj;
}
“就这么简单?” Timothy 问。
“就是这么简单,” Margaret 点头说。“这不是魔法创造,只是管道工程:它创建一个桶,连接管道(
setPrototypeOf),填充数据(apply),然后交给你。”
Timothy 验证了自己的假设:
console.log(typeof Book); // "function"
console.log(Object.getPrototypeOf(myBook) === Book.prototype); // true
“哈!类不过是一个函数!” 他大喊。“而且链接是真实存在的。”
要点
Timothy 意识到,现代的类语法只是对一直存在的基于原型的机制提供了一种便利的简写。虽然 class 关键字提升了可读性并把相关逻辑组织在一起,但它并没有引入全新的对象模型——底层仍然是创建函数并通过原型链接对象。理解这一点有助于避免对“实例”和 new 操作符的误解,提醒开发者关注引擎实际执行的过程。