JavaScript进阶篇——第七章 原型与构造函数核心知识
目录
1. 构造函数基础
2. 原型对象(prototype)
3. constructor 属性
4. 对象原型(proto)
5. 原型链机制
6. 复习要点速查表
本文系统讲解了JavaScript构造函数与原型机制。要点包括:1)构造函数封装实例属性和方法,但会造成方法重复创建;2)通过prototype原型对象实现方法共享,节约内存;3)constructor属性保持原型与构造函数的关联;4)proto__形成原型链,实现方法查找机制;5)完整原型链结构为实例→构造函数原型→Object原型→null。最佳实践是将方法定义在原型上、属性放在构造函数中,避免直接使用__proto,重写原型时需修复constructor指向。原型系统通过三大关系(构造函数-原型-实例)实现面向对象特性,有效解决内存浪费问题。
1. 构造函数基础
1.1 封装特性
构造函数是实现面向对象封装的核心工具
// ✅ 构造函数封装数据和方法
function Star(uname, age) {this.uname = uname;this.age = age;this.sing = function() {console.log('我会唱歌');}
}// 创建实例对象
const ldh = new Star('刘德华', 18);
const zxy = new Star('张学友', 19);ldh.sing(); // "我会唱歌"
zxy.sing(); // "我会唱歌"// ❗ 重点:实例对象彼此独立,互不影响
ldh.age = 20;
console.log(zxy.age); // 19(不受影响)
1.2 内存浪费问题
// ⚠️ 问题:每个实例都创建自己的方法副本
console.log(ldh.sing === zxy.sing); // false// 内存结构图示:
// | 实例1 | → | sing方法1 |
// | 实例2 | → | sing方法2 |
// 相同功能的方法被重复创建,浪费内存
2. 原型对象(prototype)
2.1 原型是什么
每个构造函数都有一个
prototype
属性,指向原型对象
function Star(uname, age) {this.uname = uname;this.age = age;
}// ✅ 访问构造函数的原型对象
console.log(Star.prototype); // 原型对象(最初为空)// ✅ 在原型上添加共享方法
Star.prototype.sing = function() {console.log('我会唱歌');
}const ldh = new Star('刘德华', 18);
const zxy = new Star('张学友', 19);// 验证方法共享
console.log(ldh.sing === zxy.sing); // true(共享同一个方法)
2.2 原型的作用
-
共享方法:所有实例共享原型上的方法
-
节约内存:方法只创建一次,所有实例共用
-
扩展功能:可随时向原型添加新方法
2.3 this指向
构造函数和原型方法中的
this
都指向实例对象
let that;
function Person(name) {this.name = name;that = this; // this指向即将创建的实例
}const o = new Person('小明');
console.log(that === o); // true// 原型方法中的this
Person.prototype.sayHello = function() {console.log(`你好,我是${this.name}`);
}o.sayHello(); // "你好,我是小明"
3. constructor 属性
3.1 作用与位置
每个原型对象都有
constructor
属性,指向关联的构造函数
function Star(name) {this.name = name;
}console.log(Star.prototype.constructor === Star); // trueconst ldh = new Star('刘德华');
console.log(ldh.constructor === Star); // true
3.2 使用场景
当重写整个原型对象时,需要手动设置
constructor
function Star(name) {this.name = name;
}// ❌ 错误写法:重写原型会丢失constructor
Star.prototype = {sing: function() {console.log("唱歌");},dance: function() {console.log("跳舞");}
};
console.log(Star.prototype.constructor); // 指向Object(错误)// ✅ 正确写法:手动添加constructor
Star.prototype = {constructor: Star, // 修复constructor指向sing: function() {console.log("唱歌");},dance: function() {console.log("跳舞");}
};
console.log(Star.prototype.constructor === Star); // true
4. 对象原型(proto)
4.1 作用与原理
每个实例对象都有
__proto__
属性,指向构造函数的原型对象
function Star(name) {this.name = name;
}const ldh = new Star('刘德华');// 对象原型关系
console.log(ldh.__proto__ === Star.prototype); // true
console.log(ldh.__proto__.constructor === Star); // true
4.2 原型链机制
// 原型链查找过程:
// 1. 访问ldh.sing()
// 2. 先在ldh实例上查找 → 未找到
// 3. 通过ldh.__proto__找到Star.prototype
// 4. 在Star.prototype上找到sing方法并执行// ❗ 重点:这就是为什么实例能访问原型上的方法
4.3 注意事项
-
__proto__
是非标准属性(实际开发中避免直接使用) -
标准属性是
[[Prototype]]
,与__proto__
意义相同 -
可以使用
Object.getPrototypeOf()
获取原型 -
__proto__
对象原型里也有constructor
属性
// 推荐:使用标准方法获取原型
console.log(Object.getPrototypeOf(ldh) === Star.prototype); // true
5. 原型链机制
5.1 完整原型链
text实例对象 (ldh)↓ __proto__
构造函数原型 (Star.prototype)↓ __proto__
Object原型 (Object.prototype)↓ __proto__
null
5.2 原型链验证
function Star() {}
const ldh = new Star();// 验证原型链
console.log(ldh.__proto__ === Star.prototype); // true
console.log(Star.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
6. 复习要点速查表
核心概念关系
概念 | 位置 | 指向 | 作用 |
---|---|---|---|
prototype | 构造函数上 | 原型对象 | 存放共享方法 |
proto | 实例对象上 | 构造函数的prototype | 实现原型链查找 |
constructor | 原型对象上 | 构造函数 | 表明原型对象的来源 |
原型系统三大关系
-
构造函数 ↔ 原型对象
构造函数.prototype = 原型对象
原型对象.constructor = 构造函数
-
实例对象 ↔ 原型对象
实例对象.__proto__ = 构造函数.prototype
-
原型对象 ↔ Object原型
构造函数.prototype.__proto__ = Object.prototype
高频面试题解答
-
prototype是什么?
构造函数自动拥有的原型对象,用于存放共享方法 -
constructor属性在哪里?
存在于原型对象和对象原型中,指向创建它们的构造函数 -
__proto__属性在哪里?指向谁?
在每个实例对象中,指向构造函数的原型对象 -
如何解决构造函数的内存浪费问题?
将方法定义在原型对象上,实现方法共享 -
原型链的终点是什么?
null
(Object.prototype.proto === null)
最佳实践
-
方法放在原型上:节约内存,实现共享
-
属性放在构造函数中:保持实例独立性
-
避免直接使用__proto__:使用
Object.getPrototypeOf()
-
重写原型时修复constructor:保持正确的构造函数指向
// ✅ 推荐模式
function Person(name) {// 实例属性this.name = name;
}// 原型方法
Person.prototype.sayHello = function() {console.log(`你好,我是${this.name}`);
};// 创建实例
const p = new Person('小明');
p.sayHello();
记忆口诀:
"构造函数造实例,prototype存共享"
"__proto__连原型,constructor指根源"
"原型链上找方法,层层查找不迷路"
"方法共享省内存,面向对象真强大"