【前端面试小册】JS-第10节:面试必会 - 原型和原型链
一、核心概念
1.1 三个关键点
- 每个普通函数都有一个
prototype属性,箭头函数没有prototype属性 - 每一个 JavaScript 对象(除了
null)都具有隐式原型__proto__属性,这个属性指向构造函数的显式原型prototype - 对象本身没有的属性,会去它的隐式原型
__proto__(也就是它的构造函数的显式原型prototype)中寻找
类比理解:原型链就像家族族谱。每个对象都有自己的"父亲"(__proto__),而构造函数定义了"家族基因"(prototype)。当你找不到某个属性时,就会沿着族谱向上查找,直到找到或到达顶层。
二、prototype(显式原型)
2.1 基本概念
每个普通函数都有一个 prototype 属性,这个属性指向一个对象。
function Person() {}
// 函数拥有 prototype 属性
Person.prototype.name = '前端职场圈';
var p1 = new Person();
var p2 = new Person();
console.log(p1.name); // 前端职场圈
console.log(p2.name); // 前端职场圈
console.log(Person.prototype); // { name: '前端职场圈', constructor: Person, ... }
2.2 prototype 的来源
prototype 指向的对象是调用该构造函数而创建的实例的原型。
理解方式:
// 等价理解
p1 = Object.create({ name: '前端职场圈' });
p2 = Object.create({ name: '前端职场圈' });
核心理解:
- 每一个 JavaScript 对象(
null除外)在创建的时候就会与之关联另一个对象 - 这个对象就是我们所说的原型
- 每一个对象都会从原型"继承"属性
2.3 关系图
graph TD
A[构造函数 Person] --> B[prototype 属性]
B --> C[原型对象]
D[实例 p1] --> E[__proto__]
E --> C
F[实例 p2] --> G[__proto__]
G --> C
C --> H[共享属性和方法]
三、proto(隐式原型)
3.1 基本概念
每一个 JavaScript 对象(除了 null)都具有一个隐式属性 __proto__,指向该对象的原型。
function Person() {}
Person.prototype.name = '前端职场圈';
const p = new Person();
console.log(p.name); // 前端职场圈
console.log(p.__proto__); // { name: '前端职场圈', constructor: Person, ... }
console.log(p.__proto__ === Person.prototype); // true
3.2 属性查找机制
当你查找对象某个属性不存在时,它会去隐式原型 __proto__ 中寻找,等价于去它的构造函数的显式原型 prototype 中寻找。
查找流程:
graph TD
A[访问 p.name] --> B{p 自身有 name?}
B -->|有| C[返回 p.name]
B -->|无| D[查找 p.__proto__]
D --> E{Person.prototype 有 name?}
E -->|有| F[返回 Person.prototype.name]
E -->|无| G[继续向上查找]
G --> H[Object.prototype]
H --> I{有 name?}
I -->|有| J[返回 Object.prototype.name]
I -->|无| K[返回 undefined]
代码验证:
function Person() {}
Person.prototype.name = '前端职场圈';
const p = new Person();
// p 本身没有 name 属性
console.log(p.hasOwnProperty('name')); // false
// 通过 __proto__ 查找到原型上的 name
console.log(p.name); // 前端职场圈
// __proto__ 等价于构造函数的 prototype
console.log(p.__proto__ === Person.prototype); // true
四、原型链
4.1 原型链的概念
原型链是 JavaScript 实现继承的机制。当对象查找属性时,如果自身没有,就会沿着 __proto__ 向上查找,直到找到或到达顶层 null。
function Person() {}
Person.prototype.name = '前端职场圈';
var p = new Person();
console.log(p.name); // 前端职场圈
console.log(p.__proto__); // Person.prototype
查找路径:
graph TD
A[实例 p] --> B[查找 name]
B --> C{p 自身有?}
C -->|无| D[p.__proto__]
D --> E[Person.prototype]
E --> F{有 name?}
F -->|有| G[返回 前端职场圈]
F -->|无| H[Person.prototype.__proto__]
H --> I[Object.prototype]
I --> J{有 name?}
J -->|有| K[返回 Object.prototype.name]
J -->|无| L[Object.prototype.__proto__]
L --> M[null]
M --> N[返回 undefined]
4.2 完整的原型链
function Person() {}
const p = new Person();
// 完整的原型链
console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
经典原型链图:
实例 p
↓ __proto__
Person.prototype
↓ __proto__
Object.prototype
↓ __proto__
null
五、原型进阶
5.1 面试题一:原型链查找
var foo = {},
F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';
console.log(foo.a); // value a
console.log(foo.b); // undefined
console.log(F.a); // value a
console.log(F.b); // value b
分析:
graph TD
A[foo 是对象] --> B[foo.__proto__ === Object.prototype]
B --> C[找到 a: value a]
D[foo.__proto__] --> E[Object.prototype]
E --> F[Object.prototype.__proto__ === null]
F --> G[找不到 b,返回 undefined]
H[F 是函数] --> I[F.__proto__ === Function.prototype]
I --> J[找到 b: value b]
K[F.__proto__] --> L[Function.prototype]
L --> M[Function.prototype.__proto__ === Object.prototype]
M --> N[找到 a: value a]
详细解释:
foo.a:foo是对象,foo.__proto__ === Object.prototype,找到afoo.b:foo的原型链中没有b,返回undefinedF.a:F是函数,F.__proto__ === Function.prototype,Function.prototype.__proto__ === Object.prototype,找到aF.b:F.__proto__ === Function.prototype,找到b
5.2 构造器
function Person(name) {
this.name = name;
}
console.log(Person.__proto__ === Function.prototype); // true
理解:函数的 __proto__ 指向它的构造函数的原型对象,所以函数 Person 的构造函数是 Function。
5.3 9 个内置构造器
JavaScript 有 9 个内置构造器:
Number;
Boolean;
String;
Object;
Function;
Array;
RegExp;
Error;
Date;
// ES6 新增
Proxy;
5.4 构造器的构造器是 Function
console.log(Number.__proto__ === Function.prototype); // true
console.log(Boolean.__proto__ === Function.prototype); // true
console.log(String.__proto__ === Function.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true
console.log(Array.__proto__ === Function.prototype); // true
console.log(RegExp.__proto__ === Function.prototype); // true
console.log(Error.__proto__ === Function.prototype); // true
console.log(Date.__proto__ === Function.prototype); // true
console.log(Date.__proto__ === Object.prototype); // false
关键理解:
- 构造器是对象的同时也是特殊的函数
- 所有构造器都继承自
Function.prototype - 所以它们的
__proto__都指向Function.prototype
5.5 内置对象
console.log(Math.__proto__ === Object.prototype); // true
console.log(JSON.__proto__ === Object.prototype); // true
console.log(JSON.__proto__ === Function.prototype); // false
原因:Math、JSON 是以对象形式存在,不用 new,所以它们的构造器是 Object。
5.6 独特的 Function.prototype
特殊之处:Function.prototype 是函数类型,而不是对象类型!
// 你以为的结论
console.log(typeof Function.prototype); // "function" ❌ 不是 "object"
// 其他函数的 prototype 都是对象
console.log(typeof Number.prototype); // "object"
console.log(typeof Boolean.prototype); // "object"
console.log(typeof String.prototype); // "object"
console.log(typeof Object.prototype); // "object"
console.log(typeof Array.prototype); // "object"
console.log(typeof RegExp.prototype); // "object"
console.log(typeof Error.prototype); // "object"
console.log(typeof Date.prototype); // "object"
原因:Function.prototype 是特殊的存在,只有它的 Function.prototype 是函数,其余函数的 prototype 类型都是对象。
5.7 Function.prototype.proto
虽然 Function.prototype 是函数类型,但函数也是对象,所以它也有 __proto__:
console.log(Function.prototype.__proto__ === Object.prototype); // true
理解:
- 函数是一等公民,函数也是对象
- 所以
Function.prototype被视为一个对象就有__proto__ Function.prototype.__proto__指向Object.prototype
六、总结
核心知识点
-
prototype(显式原型):
- 每个普通函数都有
prototype属性 - 指向一个对象,是实例的原型
- 每个普通函数都有
-
proto(隐式原型):
- 每个对象都有
__proto__属性 - 指向构造函数的
prototype
- 每个对象都有
-
原型链:
- 对象查找属性时,沿着
__proto__向上查找 - 直到找到或到达
null
- 对象查找属性时,沿着
-
特殊点:
- 所有构造器都来自
Function.prototype - 构造器既是函数又是对象
Function.prototype是函数类型(唯一例外)Function.prototype.__proto__ === Object.prototype
- 所有构造器都来自
常见面试题
Q1: 什么是原型链?
答:原型链是 JavaScript 实现继承的机制。当对象查找属性时,如果自身没有,就会沿着 __proto__ 向上查找,直到找到或到达顶层 null。
Q2: prototype 和 proto 的区别?
答:
prototype:函数才有的属性,指向原型对象__proto__:对象才有的属性,指向构造函数的prototype
Q3: 为什么 Function.prototype 是函数类型?
答:这是 JavaScript 的特殊设计。Function.prototype 是唯一一个 prototype 是函数类型的,其他函数的 prototype 都是对象类型。
实战建议
- ✅ 理解原型链有助于理解 JavaScript 的继承机制
- ✅ 掌握
prototype和__proto__的关系 - ✅ 理解原型链查找机制,有助于调试和性能优化
- ✅ 注意
Function.prototype的特殊性
每天更新3-4节,持续更新中... 目标:50天学完,上岸银行总行!


