前言
Hello~大家好。我是秋天的一阵风
今天咱们来聊聊JavaScript中一个超级重要的概念------原型和原型链。你是不是在学习JavaScript的时候,经常听到这两个词,但又觉得有点摸不着头脑呢?
今天我想把我的学习经验和大家分享一下,希望能帮你少走弯路。如果你看完这篇还没搞懂,可以随时评论或者私信我。只要跟着我一步步来,你肯定能掌握这个知识点。准备好了吗?让我们一起揭开原型和原型链的神秘面纱吧!
一、什么是原型、原型链?
在深入探讨原型和原型链之前,我想先聊聊大家在学习过程中可能遇到的困惑。相信你在阅读其他文章时,也见过各种各样的表述:一会儿是proto
,一会儿是prototype
,还有原型对象
......这些不同的称呼,很容易让人摸不着头脑,把一个原本并不复杂的话题搞得复杂起来。
为了避免这种混乱和更好地帮你理清思路,在后续的内容中,我们会直接使用英文术语来表示,这样既能保持一致性,也能让你更准确地理解这些概念。
1. prototype : 原型对象, 函数的一个属性,它的本质是对象:{}
每一个 函数 都有一个
prototype属性
,这个属性的值是一个对象。
js
function Test() {
this.a = 1;
}
console.log("-------Test.prototype-------------");
console.log(Test.prototype); // {}
console.log(Object.prototype.toString.call(Test.prototype)); // [object Object]

2. proto: 原型属性,对象的一个属性,它的本质也是一个对象:{}
每一个 对象 都有一个
__proto__属性
,这个属性的值也是一个对象。
js
const a = {};
console.log(a) // {}
console.log(a.__proto__)
console.log(Object.prototype.toString.call(a.__proto__)); // [object Object]

3. 一个对象的__proto__ 保存着 该对象的构造函数的 prototype
是不是觉得有点绕?没关系,我用代码给你演示:
Test
是一个构造函数,所以它身上有一个prototype
属性test
对象是通过Test构造函数生成的一个实例对象,所以它身上有一个__proto__
属性
简单来说就是: 一个对象的__proto__
属性的值 等于 这个对象的构造函数的prototype属性的值
js
function Test() {
this.a = 1;
}
const test = new Test();
console.log(test.__proto__ === Test.prototype); // true
4. 提出几个问题,深入理解
在了解完上面的相等关系以后,我们来思考一个问题,既然说每个对象都有一个__proto__
属性,值是一个对象。
那么Test.prototype
不也是一个对象吗? Test.prototype
有__proto__
属性吗,答案是肯定的。
js
console.log(Test.prototype.__proto__); // {}

我们继续提出第二个问题,这个Test.prototype.__proto__
指向的是不是应该是构造它的函数的prototyype
?
也就是 Test.prototype.__proto__ === Test.prototype的构造函数.protoype
当然,我直接告诉你答案,
js
Test.prototype.__proto__ === Object.prototype // true
看到这里,估计你应该会举一反三地提出第三个问题,Object.prototype
也是一个对象,那它的__proto__
属性指向谁呢? 答案是null
,因为这已经是最顶层了。
js
console.log(Object.prototype.__proto__); //null,顶层
5. 总结
我们把上面的案例代码都完整列出来,并且同学们重新回顾一遍加深印象:
js
function Test() {
this.a = 1;
}
console.log("-------Test.prototype-------------");
console.log(Test.prototype);
const test = new Test();
console.log(test);
console.log(test.__proto__ === Test.prototype); // true
console.log(Test.prototype.__proto__ === Object.prototype); //true
console.log(Object.prototype.__proto__); //null,顶层

二、 那什么是原型链呢?
我们先看一个代码:
js
function Test() {
this.a = 1;
}
Test.prototype.b = 2;
console.log("Test.prototype: ", Test.prototype);
const test = new Test();
console.log(test.b); // 2
console.log("test: ", test);
Object.prototype.c = 3;
console.log(test.c) // 3
-
我们给
Test
构造函数的prototype
对象赋值了一个b
属性,值为2 -
再给
Object
构造函数的prototype
对象赋值了一个c
属性,值为3 -
然后在
test
对象上直接访问b
和c
,发现是都可以访问到的,这是为什么呢? -
我们看看控制台:

其实,查找b和c的过程如下:
原型链的查找过程
- 自身属性查找:首先在对象自身查找属性或方法。
- 原型链查找 :如果自身没有找到,沿着
__proto__
属性向上查找,直到找到该属性或方法,或者到达原型链的末端(null
)。
所以我们可以给原型链下一个定义:
- 当你访问一个对象的属性或方法时,如果该对象本身没有该属性或方法,JavaScript 会沿着
__proto__
属性向上查找,直到找到该属性或方法,或者到达原型链的末端(即null
)。 - 这个查找过程形成了一个"链",称为原型链。
三、Function 和 Object的特殊性
1. Function
我们还是以上面的代码来当测试案例:
js
function Test() {
this.a = 1;
}
const test = new Test();
console.log("test: ", test);
首先Test
是一个函数,这一点是毋庸置疑的。但是在Javascript
的世界里呢,函数是对象的一种特殊类型。函数(Function)不仅仅是代码块,它们也是对象(Object)。这意味着函数可以像普通对象一样具有属性和方法,并且可以被动态地操作。
既然是这样,我们像之前一样"套公式":只要是对象,就有一个__proto__
属性,等于它的构造函数的prototype
对象
Test
被当成对象时,其实它的构造函数就是Function
,你可以这么理解:
const Test => new Function()
所以我们可以得出一个结论:
js
console.log(Test.__proto__); // ƒ () { [native code] }
console.log(Test.__proto__ === Function.prototype); //true
Function
自己本身也是一个函数,所以我们可以继续套公式得出结论:
js
//Function 本身也是一个函数
console.log(Function); //ƒ Function() { [native code] }
console.log(Function.__proto__); // ƒ () { [native code] }
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Function.__proto__ === Function.prototype); //true
2. Object
我们进行这么一个推理,平时我们生成对象都是采用字面量的形式,也就是:const obj = {};
那么其实 const obj = {} 等于 const obj = new Object()
;
并且Object
也是一个Function
:
javascript
console.log(typeof Object); //function
那么Object
是不是跟Test
一样,具备以下的关系:
js
//推理
// const obj = {} 等于 const obj = new Object()
console.log(typeof Object); //function
console.log(Object.__proto__ === Function.prototype); //true
console.log(Object.__proto__ === Function.__proto__); //true
四、 constructor
constructor
就比较简单了,它是实例对象上的一个属性,指向它的构造函数:
js
console.log("---------constructor----------");
console.log(test.constructor);
/**
* ƒ Test() {
* this.a = 1;
* }
* */
console.log(test.constructor === Test); //true
//构造函数本身是函数,相当于函数的prototype
console.log(test.constructor.prototype === Test.prototype); //true
五、测试题目
最后,我们来看一个简答题:
js
console.log("简答题:");
function Foo() {}
const foo = new Foo();
// Foo.__proto__ = ?
// foo.__proto__ = ?
// Foo.prototype.__proto__ = ?
Foo
是一个函数,也是一个对象,它的构造函数是Function
。foo
是Foo
的实例对象,它的构造函数是Foo
。Foo.protootype
也是一个普通对象,它的构造函数是Object
。
所以答案如下:
js
console.log("简答题:");
function Foo() {}
const foo = new Foo();
// Foo.__proto__ = ?
// foo.__proto__ = ?
// Foo.prototype.__proto__ = ?
console.log(Foo.__proto__ === Function.prototype); // true
console.log(foo.__proto__ === Foo.prototype); // true
console.log(Foo.prototype.__proto__ === Object.prototype); // true