核心思想差异:
-
Java的构造函数:基于严格的“类”(Class),像一个工业模具。一旦设计好,就按照这个模具精确地生产出一模一样的产品。
-
JavaScript的构造函数:基于灵活的“原型”(Prototype),更像一个手工作坊。有一个核心的师傅(构造函数),还有一个所有产品都能使用的公共“工具墙”(原型)。
1. Java:工业模具 (class
)
在Java中,你必须先定义一个 class
。这个 class
是一个非常严格和完整的蓝图,它详细规定了对象应该有哪些属性(字段)和行为(方法)。**构造函数(Constructor)**只是这个蓝图中的一个特殊部分,它的唯一职责是在用模具生产出产品时,进行“初始化填充”。
【示例】
// 1. 定义一个精确的工业模具:Person Class
public class Person {// 模具规定了产品必须有一个 name 属性public String name;// 构造函数:模具的“注料口”,负责在创建时将初始材料("Alice")注入public Person(String initialName) {this.name = initialName;}// 模具规定了产品必须有一个 sayHello 的行为public void sayHello() {System.out.println("Hello, I am " + this.name);}
}// 2. 使用模具进行生产
// new Person("Alice") 就是用 Person 模具生产一个实例,并通过构造函数注入"Alice"
Person alice = new Person("Alice");
alice.sayHello(); // 输出: Hello, I am Alice
总结 (Java):
-
new
的是class
(模具)。 -
构造函数是
class
的一部分,负责初始化。 -
产出的是一个严格按照
class
蓝图定义的对象实例。
2. JavaScript:手工作坊 (function
+ prototype
)
在JavaScript(经典原型模式下),没有 class
这个刚性模具。你只有一个**“师傅”(构造函数)和一个“公共工具墙”(原型 prototype
)**。
-
构造函数
function Person(name){...}
:这是**“师傅”本人**。他的任务是承接一个订单,拿到一个空的毛坯房(新对象),然后做一些个性化的装修(比如在墙上刻上名字this.name = name
)。 -
原型
Person.prototype
:这是作坊里的**“公共工具墙”**。上面挂着所有产品都能使用的工具(比如sayHello
这个方法)。把工具挂在这里的好处是,不用给每个产品都单独配一套工具,节省了材料(内存)。
【示例】
// 1. 定义“师傅”的工作内容 (构造函数)
function Person(name) {// 师傅接到新对象后,进行个性化装修:刻上名字this.name = name;
}// 2. 在“公共工具墙”上挂一个新工具并命名为 sayHello (在原型上定义方法)(最后有解释)
Person.prototype.sayHello = function() {console.log(`你好!我是 ${this.name}。`);
};// 3. 让师傅开始工作,生产一个新产品
// new Person("Alice") 就是让 Person 师傅创建一个新产品,并刻上名字"Alice"
const alice = new Person("Alice");// 4. 使用产品
// 当调用 alice.sayHello() 时,alice 自己身上没有这个工具,
// 于是它会回到师傅的“公共工具墙”(Person.prototype)上找到并使用这个工具。
alice.sayHello(); // 输出: 你好!我是 Alice。
总结 (JavaScript):
-
new
的是一个普通函数,但new
这个动作赋予了它“构造”的能力。 -
构造函数负责处理每个实例独有的属性。
-
prototype
负责存放所有实例共享的方法。 -
产出的是一个对象,这个对象通过一条看不见的线(原型链)连接着那个“公共工具墙”。
new
关键字到底做了什么?一张图看懂区别
在 Java 中 (new Person(...) ) |
在 JavaScript 中 (new Person(...) ) |
|
第一步 | 寻找 Person 类的定义(找模具) |
创建一个通用的、空的对象 {} |
第二步 | 为 Person 对象分配内存 |
将空对象的__proto__ 指向Person.prototype (连接到工具墙) |
第三步 | 调用 Person 构造方法初始化对象 |
调用Person 函数,this 指向新对象(让师傅装修) |
第四步 | 返回初始化完毕的对象引用 | 返回装修好的新对象 |
核心差异
Java 是预先规划、静态定义的。你必须先设计好完整的 class
模具,才能生产。
JavaScript 是动态、灵活的。你有一个负责个性化加工的函数,和一个可以随时增删工具的公共原型。这种“组合”的方式来定义一类对象。虽然现在ES6引入了 class
语法,但其底层原理依然是基于原型的这套“作坊模式”。
Person.prototype.sayHello = function() { console.log(`你好!我是 ${this.name}。`); };
把它看作一个句子来解读:
-
Person
: 这是我们自定义的构造函数。在JavaScript中,函数本身也是一个对象。既然是对象,它就可以拥有自己的属性。 -
.
(点): 这是对象属性的访问符。我们准备访问Person
对象上的一个属性。 -
prototype
: 这就是最关键的部分。prototype
(原型) 是每一个函数对象天生就有的一个属性。你不需要自己创建它,只要你定义一个函数,JavaScript就会自动为这个函数配上一个.prototype
属性。-
这个
.prototype
属性的值,本身也是一个对象。默认情况下,它是一个近乎空的对象。 -
可以把
Person
函数想象成一个“作坊”的品牌名,那么Person.prototype
就是这个作坊里那面真实存在的“公共工具墙”。
-
-
.sayHello
: 这是我们正在给Person.prototype
这个“工具墙”对象添加一个新属性,属性名由我们自定义为sayHello
。这就像在工具墙上钻一个孔,准备挂工具。 -
= function() { ... }
: 这是我们赋给sayHello
属性的值。因为这个值是一个函数,所以sayHello
就成了一个方法。这就像把一个真正能用的“打招呼”工具挂在了墙上。
所以,整行代码的真正含义是:
“找到
Person
这个函数对象,访问它自带的那个名叫prototype
的属性(它是一个对象),然后给这个prototype
对象新增一个名叫sayHello
的属性,并让这个属性的值等于我们定义的这个新函数。”