谈谈你对 原型、原型链、构造函数、实例、继承的理解
构造函数(constuctors):为了批量创建对象,我们会用首字母大写的函数来定义构造函数,functon Person(name,age){ this.name = name;this.age = age; }
实例(instances):通过构造函数创建的对象,就是该构造函数的实例 let xiaoming = new Person("小明",18);
new 关键字做了什么:
kotlin
使用 new 调用时会做四件事:
创建一个空对象
把这个空对象的 __proto__ 指向构造函数的 prototype
把函数中的 this 指向这个新对象
执行函数体代码(给 this 加属性)
返回这个对象(除非手动返回其他对象)
原型(prototype):每一个构造函数身上会有一个prototype属性,这个属性指向一个对象,这个对象就是我们说的原型对象。
原型链:Preson.prototype == xiaoming.proto
markdown
注意:
1、xiaoming 本身没有 .constructor 属性,它是顺着原型链在 xiaoming.__proto__(即 Person.prototype)上找到的。所以 xiaoming.constructor 实际上指向了 Person。
2、"__proto__ 是构造函数的属性" ❌ __proto__ 是**实例**的属性
3、"prototype 是所有对象都有" ❌ 只有函数有 prototype(箭头函数除外)
4、"constructor 一定是可靠的" ❌ 如果原型被替换过(如继承中),必须手动修复
5、"原型链是从 prototype 到 prototype" ❌ 是从 __proto__ 到 __proto__ 的链
原型链查找:
javascript
当你访问 xiaoming.sayHello 时,引擎会:
查自身属性:首先检查 xiaoming 对象本身是否有 sayHello 属性。
沿链向上:如果没有,就通过 xiaoming.__proto__ 找到 Person.prototype,看这里有没有。
继续向上:如果还没有,就继续通过 Person.prototype.__proto__ 找到 Object.prototype(因为所有对象默认原型链的顶端都是它)。
直到终点:如果 Object.prototype 上还没有,就再通过 Object.prototype.__proto__ 找到 null,然后返回 undefined。
最终一句话总结
当你访问一个对象的属性或方法时,如果它自己没有,JS 就会顺着它的 __proto__ 往上找,一直找到 Object.prototype,再找不到就返回 undefined ------ 这个查找路径,就是**原型链**。
而这一切的基础是:
实例.__proto__ === 构造函数.prototype

es5的继承:
javascript
// 父类构造函数
function Animal(name) {
this.name = name;
this.type = 'animal';
}
// 父类方法放在原型上
Animal.prototype.eat = function() {
console.log(this.name + " 在吃东西");
};
// 子类构造函数
function Dog(name, breed) {
// ✅ 构造函数继承:借用父类构造函数
Animal.call(this, name); // 继承实例属性:name, type
this.breed = breed; // 子类特有属性
}
// ✅ 原型链继承:建立原型关系
Dog.prototype = Object.create(Animal.prototype);
创建了一个新的空对象
将这个新对象的 __proto__ 指向 Animal.prototype
将这个新对象赋值给 Dog.prototype
这样就建立了正确的原型链关系:
dog 实例对象
↓ __proto__
Dog.prototype (新创建的对象)
↓ __proto__
Animal.prototype
↓ __proto__
Object.prototype
↓ __proto__
null
// 注意:不能直接 Dog.prototype = new Animal()
Dog 和 Animal 会共享同一个原型对象
在 Dog.prototype 上添加方法也会影响到 Animal.prototype
Animal 的实例也会获得 Dog 特有的方法,这显然不符合继承的设计意图
// 修正 constructor 指向
Dog.prototype.constructor = Dog;
// 因为 Object.create(Animal.prototype) 创建的新对象的 constructor 属性会指向 Animal,而不是 Dog。修正后才能保证
// 因为 Dog.prototype 被重新赋值了,原来的 constructor 丢了。
// 不修复的话:dog.constructor === Animal ❌
// 修复后:dog.constructor === Dog ✅
// 子类自己添加方法
Dog.prototype.bark = function() {
console.log(this.name + " 在汪汪叫");
};
// 使用
const dog = new Dog("旺财", "哈士奇");
dog.eat(); // ✅ 调用父类方法 → "旺财 在吃东西"
dog.bark(); // ✅ 调用自己的方法 → "旺财 在汪汪叫"
console.log(dog.type); // ✅ "animal",来自 Animal 构造函数
总结:组合继承的核心步骤
步骤 目的 写法
1️⃣ 构造函数继承 继承父类的实例属性 Animal.call(this, ...)
2️⃣ 原型链继承 继承父类的原型方法 Dog.prototype = Object.create(Animal.prototype)
3️⃣ 修复 constructor 保持 constructor 正确 Dog.prototype.constructor = Dog
ES6 class继承(现代标准)
scala
class Parent {
constructor(name) { this.name = name; }
say() { console.log(this.name); }
}
class Child extends Parent { // 核心: extends
constructor(name, age) {
super(name); // 核心: super 调用父类构造函数
this.age = age;
}
}
let c = new Child('Tom', 10);
c.say(); // 'Tom'