前言:本文用通俗语言 + 实战代码,拆解原型、constructor、对象原型、原型继承与原型链,带你吃透 JS 面向对象核心,轻松解决内存优化难题。
【javaScript 原型精讲】
一:构造函数
封装是面向对象思想中比较重要的一部分,js 面向对象可以通过构造函数实现的封装。
同样的将变量和函数组合到了一起并能通过 this 实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。
javascript
function Star(uname, age) {
this.uname = uname
this.age = age
this.sing = function () {
console.log('我会唱歌')
}
}
// 实例对象,获得了构造函数中封装的所有逻辑
const A = new Star('张三', 18)
const B = new Star('李四', 19)
构造函数方法很好用,但是存在浪费内存的问题
我们希望所有的对象使用同一个函数,这样就比较节省内存,那么我们就用到原型
二:原型
构造函数通过原型分配的函数是所有对象所共享的 。
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象。
这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存。
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
构造函数和原型对象中的 this 都指向实例化的对象。
javascript
function test() {
console.log("Hello, World!");
}
console.dir(test.prototype)
控制台输出:

这时我们可以通过原型让所有的对象使用同一个函数
javascript
function Star(uname, age) {
this.uname = uname
this.age = age
// this.sing = function () {
// console.log('我会唱歌')
// }
}
Star.prototype.sing = function () {
console.log('我会唱歌')
}
const A = new Star('张三', 18)
const B = new Star('李四', 19)
console.log(A.sing === B.sing) // true
最终打印结果为 true,核心原因是:我们将sing 方法挂载在了 Star 构造函数的 prototype 原型对象上,而非构造函数内部。
- 当创建 A、B 两个实例对象时,不会再为每个实例单独生成一份 sing方法,有效避免了构造函数内部定义方法造成的内存冗余问题;
- 所有 Star 的实例对象,都会通过自身的
__proto__属性,统一指向 Star.prototype 原型对象,A.sing 和 B.sing 访问的是内存中同一个函数地址; - 因此两个实例调用的是完全相同的方法,全等对比结果为 true,这也是原型实现方法共享、节省内存的核心体现。
三:constructor
每个原型对象里面都有个 constructor 属性(constructor 构造函数)
作用:该属性指向该原型对象的构造函数
javascript
function test() {
console.log("Hello, World!");
}
const A = new test()
console.log(Star.prototype.constructor );
控制台输出:

需要注意的是,如果有多个对象的方法,我们可以给原型对象采取对象形式赋值。
但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。
此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
javascript
Star.prototype = {
constructor: A,
sing:function() {
console.log("La la la");
},
dance:function() {
console.log("Dancing...");
}
}
四:对象原型
对象都会有一个属性 __proto__,指向构造函数的 prototype 原型对象,之所以我们对对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。
javascript
function test() {
console.log("Hello, World!");
}
const A = new test()
console.log(A.__proto__ === test.prototype) // true
__proto__ 是 JS 非标准属性
\[prototype\]\] 和 `__proto__` 意义相同 用来表明当前实例对象指向哪个原型对象 prototype `__proto__` 对象原型里面也有一个 constructor 属性,指向创建该实例对象的构造函数 ```javascript function test() { console.log("Hello, World!"); } const A = new test() console.log(A.__proto__.constructor === test) // true ``` ### 五:原型继承 在 JavaScript 里,没有真正意义上的类继承,我们的继承都是依靠原型实现的,这就是 JavaScript 最核心的原型继承。 简单说:让一个构造函数的原型,指向另一个构造函数的实例,就能实现方法和属性的继承。 核心思想 子构造函数的原型 = 父构造函数的实例 这样一来,子类实例就能通过 `__proto__` 找到父类原型上的所有方法,实现复用 + 继承。 ```javascript // 1. 父构造函数:人 function Person() { this.type = '人类'; } // 父方法放在原型上 Person.prototype.eat = function () { console.log('我会吃饭'); }; // 2. 子构造函数:学生 function Student(name) { this.name = name; } // 关键:实现原型继承 Student.prototype = new Person(); // 手动修正 constructor 指向(必须写!) Student.prototype.constructor = Student; // 给学生添加自己的独有方法 Student.prototype.study = function () { console.log('我会学习'); }; // 测试 const s1 = new Student('小明'); console.log(s1.type); // 人类(继承自 Person) s1.eat(); // 我会吃饭(继承自 Person 原型) s1.study(); // 我会学习(自己的方法) ``` ### 六:原型链 原型链是 JavaScript 查找属性 / 方法的规则,也是 JS 面向对象最底层的运行机制, 当你访问一个对象的属性 / 方法时: 先在对象自身找 找不到 → 去 `__proto__` 找(指向构造函数原型 prototype) 还找不到 → 继续去原型的 `__proto__` 找 直到 null 为止 这条一层层向上查找的链路,就是原型链。 标准原型链结构 ```javascript 实例对象 __proto__ → 构造函数原型 __proto__ → Object原型 __proto__ → null ``` 图解:  最后: 如果我的内容对你有帮助,请点赞,评论,收藏,创作不易。大家的支持就是我坚持下去的动力! 