javascript中的三角关系
原型就像对象的「妈妈」,会把自己有的东西(属性 / 方法)「遗传」给孩子。比如所有的狗都会摇尾巴,但我们没必要给每只狗都写一遍摇尾巴的方法,只需要让「狗妈妈」(原型)会摇尾巴,那么所有小狗(实例对象)就都天生会摇尾巴了。// 狗妈妈(原型)wagTail: function() { console.log("摇尾巴~");// 小狗(实例对象),它的妈妈是 dogPrototype// 摇尾巴~(
在 JavaScript 中,原型(Prototype)、原型链(Prototype Chain)和对象(Object)构成了一种核心的三角关系,它们共同支撑了 JavaScript 的继承机制。理解这三者的关系是掌握 JS 面向对象编程的关键。
对象、构造函数、原型三者构成了一个三角关系,核心逻辑如下:
-
构造函数与原型:
- 每个构造函数(如
function Person() {}
)都有一个prototype
属性,指向其 “原型对象”。 - 原型对象中有一个
constructor
属性,反向指向其对应的构造函数(Person.prototype.constructor === Person
)。
- 每个构造函数(如
-
实例对象与原型:
- 通过构造函数创建的实例对象(如
let p = new Person()
),其__proto__
属性指向构造函数的原型对象(p.__proto__ === Person.prototype
)。
- 通过构造函数创建的实例对象(如
-
实例与构造函数:
- 实例对象通过原型链间接关联到构造函数(
p.constructor === Person
,实际是从原型对象继承的constructor
属性)。
- 实例对象通过原型链间接关联到构造函数(
在这样的描述下你可能觉得云里雾里,没关系我们通过讲故事的方式把他们梳理一遍。
1、什么是对象
首先,你需要明白什么是对象:你可以把对象想象成一个「盒子」,里面装着一些「东西」(属性,比如名字、年龄)和「动作」(方法,比如吃饭、跑步)。
比如:
let dog = {
name: "旺财", // 属性
bark: function() { console.log("汪汪!"); } // 方法
};
这个dog就是一个对象,里面㕛名字和叫的动作。
2. 什么是「原型」?
原型就像对象的「妈妈」,会把自己有的东西(属性 / 方法)「遗传」给孩子。
比如所有的狗都会摇尾巴,但我们没必要给每只狗都写一遍摇尾巴的方法,只需要让「狗妈妈」(原型)会摇尾巴,那么所有小狗(实例对象)就都天生会摇尾巴了。
// 狗妈妈(原型)
let dogPrototype = {
wagTail: function() { console.log("摇尾巴~"); }
};
// 小狗(实例对象),它的妈妈是 dogPrototype
let dog1 = Object.create(dogPrototype);
let dog2 = Object.create(dogPrototype);
dog1.wagTail(); // 摇尾巴~(从妈妈那继承的)
dog2.wagTail(); // 摇尾巴~(同样从妈妈那来的)
3. 什么是「原型链」?
如果小狗想做一件事,自己不会,就会问妈妈;妈妈不会,就会问外婆;外婆不会,就会问太外婆…… 一直问到「老祖宗」(null
)为止。这个「代代相传」的链条就是原型链。
- 小狗(
dog
)想toString()
(把自己变成字符串),但它自己不会。 - 就问妈妈(
dogPrototype
),妈妈也不会。 - 妈妈就问外婆(
Object.prototype
,所有对象的老祖宗之一),外婆会!于是小狗就会了。
console.log(dog1.toString()); // "[object Object]"(从外婆那学的)
4. 三角关系:对象、构造函数、原型
用「生孩子」来比喻:
- 构造函数 就像「妈妈的肚子」,专门用来「生对象」的(比如
new Person()
生出人对象)。 - 原型 是「妈妈」,构造函数会把这个「妈妈」指定为所有孩子的原型(
Person.prototype
就是人对象的妈妈)。 - 实例对象 是「孩子」,天生就认这个妈妈(
孩子.__proto__ === 妈妈
)。
而且:
- 妈妈(原型)知道自己是哪个肚子生的(
妈妈.constructor === 构造函数
)。 - 孩子也知道自己的「出生证明」是哪个构造函数(
孩子.constructor === 构造函数
,其实是继承了妈妈的记忆)。
让我们举个例子,更深刻的理解
// 1. 构造函数(相当于"生产线",专门用来创建学生对象)
function Student(name) {
this.name = name; // 每个学生有自己的名字(实例属性)
}
// 2. 原型对象(相当于"共享技能库",所有学生都能共用这里的东西)
Student.prototype = {
constructor: Student, // 告诉原型:"我属于Student这个构造函数"
study: function() { // 所有学生都会学习(共享方法)
console.log(this.name + "在学习");
},
school: "阳光小学" // 所有学生都在同一所学校(共享属性)
};
// 3. 创建实例对象(用生产线造出两个学生)
let stu1 = new Student("小明");
let stu2 = new Student("小红");
// 现在来看三角关系:
// ① 实例对象 → 原型(学生 → 共享技能库)
console.log(stu1.__proto__ === Student.prototype); // true(小明的技能库是Student原型)
console.log(stu2.__proto__ === Student.prototype); // true(小红的技能库也是Student原型)
// ② 原型 → 构造函数(共享技能库 → 生产线)
console.log(Student.prototype.constructor === Student); // true(技能库属于Student生产线)
// ③ 实例对象 → 构造函数(学生 → 生产线,通过原型间接关联)
console.log(stu1.constructor === Student); // true(小明知道自己来自Student生产线)
console.log(stu2.constructor === Student); // true(小红也知道)
// 再看原型链的作用:
// 学生自己有的属性:直接用
console.log(stu1.name); // "小明"(自己的名字)
console.log(stu2.name); // "小红"(自己的名字)
// 自己没有,但原型有的:从原型拿(共享)
stu1.study(); // "小明在学习"(用原型的study方法)
stu2.study(); // "小红在学习"(用同一个study方法)
console.log(stu1.school); // "阳光小学"(用原型的school属性)
// 原型也没有的:继续往上找(找原型的原型)
console.log(stu1.toString()); // "[object Object]"
// 上面这个方法其实在 Object.prototype 里(所有对象的"老祖宗"原型)
// 查找路径:stu1 → Student.prototype → Object.prototype → 找到toString()
总结:
- 原型是对象的 “模板”,存储共享属性和方法;
- 原型链是属性查找的路径,实现了继承;
- 构造函数、原型、实例三者通过
prototype
、__proto__
、constructor
相互关联,形成了 JavaScript 中对象系统的核心三角关系。
更多推荐
所有评论(0)