Function.prototype和Object.prototype
2025-09-28
711 字约 3 分钟
...一道面试题引发的原型链血案,彻底搞懂 Function.prototype 和 Object.prototype
前言
有这样一道题:
Function.prototype.a = 1;
Object.prototype.b = 2;
function F() {}
const f = new F();
console.log(F.a); // ?
console.log(F.b); // ?
console.log(f.a); // ?
console.log(f.b); // ?
目录
问题分析与答案揭晓
让我们先公布正确答案,然后深入分析原因:
Function.prototype.a = 1;
Object.prototype.b = 2;
function F() {}
const f = new F();
console.log(F.a); // 1 ✅
console.log(F.b); // 2 ✅
console.log(f.a); // undefined ❌ 很多人以为是1
console.log(f.b); // 2 ✅
为什么 f.a 是 undefined 而不是 1?
这是因为函数和实例的原型链路径完全不同!
原型链的本质机制
JavaScript中的两条原型链
在JavaScript中,存在两条不同的原型链:

// 函数的原型链
F.__proto__ === Function.prototype; // true
Function.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true
// 实例的原型链
f.__proto__ === F.prototype; // true
F.prototype.__proto__ === Object.prototype; // true
关键理解:F.prototype ≠ Function.prototype
这是最容易混淆的地方:
// F.prototype 是给 F 的实例用的
F.prototype.constructor === F; // true
// Function.prototype 是给函数 F 自己用的
F.__proto__ === Function.prototype; // true
函数的双重身份
身份一:作为对象的函数
// F 作为对象,它的原型链是:
// F -> Function.prototype -> Object.prototype -> null
Function.prototype.sayHello = function() {
return `Hello, I'm ${this.name}`;
};
F.sayHello(); // "Hello, I'm F"
// F 可以访问 Function.prototype 上的方法
身份二:作为构造函数的函数
// F 作为构造函数,它为实例提供原型:
// f -> F.prototype -> Object.prototype -> null
F.prototype.greet = function() {
return "Hello from instance";
};
const f = new F();
f.greet(); // "Hello from instance"
// 实例 f 可以访问 F.prototype 上的方法
实例的原型链路径
为什么实例访问不到 Function.prototype?
Function.prototype.a = 1;
Object.prototype.b = 2;
F.prototype.c = 3;
function F() {}
const f = new F();
// 沿着原型链查找过程
console.log(f.a); // undefined
/* 查找路径:
* 1. f 自身 -> 没有 a 属性
* 2. f.__proto__ (F.prototype) -> 没有 a 属性
* 3. F.prototype.__proto__ (Object.prototype) -> 没有 a 属性
* 4. Object.prototype.__proto__ (null) -> 查找结束
*
* Function.prototype 根本不在这条链上!
*/
console.log(f.b); // 2
/* 查找路径:
* 1. f 自身 -> 没有 b 属性
* 2. f.__proto__ (F.prototype) -> 没有 b 属性
* 3. F.prototype.__proto__ (Object.prototype) -> 找到 b = 2 ✅
*/
console.log(f.c); // 3
/* 查找路径:
* 1. f 自身 -> 没有 c 属性
* 2. f.__proto__ (F.prototype) -> 找到 c = 3 ✅
*/
图解原型链查找
// 创建一个更复杂的例子来理解
Function.prototype.funcMethod = 'I am from Function.prototype';
Object.prototype.objMethod = 'I am from Object.prototype';
F.prototype.instanceMethod = 'I am from F.prototype';
function F() {
this.ownProp = 'I am own property';
}
const f = new F();
// 查找顺序可视化
const searchOrder = {
'f.ownProp': {
found: 'f (own property)',
value: 'I am own property'
},
'f.instanceMethod': {
found: 'F.prototype',
value: 'I am from F.prototype'
},
'f.objMethod': {
found: 'Object.prototype',
value: 'I am from Object.prototype'
},
'f.funcMethod': {
found: 'not found',
value: undefined,
reason: 'Function.prototype not in prototype chain'
}
};
常见误区与避坑指南
误区1:混淆 F.prototype 和 Function.prototype
// ❌ 错误理解
"F 继承自 Function.prototype,所以 F 的实例也能访问 Function.prototype"
// ✅ 正确理解
"F 继承自 Function.prototype,但 F 的实例继承自 F.prototype"
误区2:认为所有对象都能访问 Function.prototype
// ❌ 错误认知
const obj = {};
console.log(obj.call); // undefined,不是 Function.prototype.call
// ✅ 正确认知
// 只有函数才能直接访问 Function.prototype
function fn() {}
console.log(fn.call); // Function.prototype.call
误区3:不理解 constructor 的指向
function F() {}
const f = new F();
// 这些都是 true,但原因不同
console.log(F.constructor === Function); // F是函数,继承自Function.prototype
console.log(f.constructor === F); // f是实例,F.prototype.constructor指向F
// 原型链路径
F.constructor === Function.prototype.constructor; // true
f.constructor === F.prototype.constructor; // true
实际开发中的应用
扩展所有函数的能力
// 给所有函数添加缓存功能
Function.prototype.cached = function() {
const cache = new Map();
const originalFn = this;
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = originalFn.apply(this, args);
cache.set(key, result);
return result;
};
};
// 使用示例
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const cachedFib = fibonacci.cached();
console.log(cachedFib(40)); // 第一次计算,较慢
console.log(cachedFib(40)); // 从缓存读取,很快
扩展所有实例的能力
// 给所有对象添加深拷贝功能
Object.prototype.deepClone = function() {
if (this === null || typeof this !== 'object') {
return this;
}
if (this instanceof Date) {
return new Date(this.getTime());
}
if (this instanceof Array) {
return this.map(item =>
typeof item === 'object' ? item.deepClone() : item
);
}
const cloned = {};
for (let key in this) {
if (this.hasOwnProperty(key)) {
cloned[key] = typeof this[key] === 'object'
? this[key].deepClone()
: this[key];
}
}
return cloned;
};
// 所有对象都可以使用
const obj = { a: 1, b: { c: 2 } };
const cloned = obj.deepClone();
检测原型链关系
// 实用的原型链检测工具
function analyzePrototypeChain(obj, name = 'obj') {
const chain = [];
let current = obj;
while (current !== null) {
chain.push({
level: chain.length,
object: current,
constructor: current.constructor?.name || 'Unknown',
isPrototype: current !== obj
});
current = Object.getPrototypeOf(current);
}
console.log(`\n=== ${name} 的原型链分析 ===`);
chain.forEach(({ level, object, constructor, isPrototype }) => {
const prefix = ' '.repeat(level);
const type = isPrototype ? '(prototype)' : '(self)';
console.log(`${prefix}${level}. ${constructor} ${type}`);
});
return chain;
}
// 使用示例
function MyClass() {}
const instance = new MyClass();
analyzePrototypeChain(MyClass, 'MyClass');
analyzePrototypeChain(instance, 'instance');
/* 输出:
=== MyClass 的原型链分析 ===
0. MyClass (self)
1. Function (prototype)
2. Object (prototype)
=== instance 的原型链分析 ===
0. MyClass (self)
1. Object (prototype)
*/
进阶:手写 instanceof
理解了原型链,我们可以手写 instanceof 操作符:
function myInstanceof(instance, Constructor) {
// 获取构造函数的原型
const prototype = Constructor.prototype;
// 获取实例的原型链起点
let current = Object.getPrototypeOf(instance);
// 沿着原型链查找
while (current !== null) {
if (current === prototype) {
return true;
}
current = Object.getPrototypeOf(current);
}
return false;
}
// 测试
function F() {}
const f = new F();
console.log(myInstanceof(f, F)); // true
console.log(myInstanceof(f, Object)); // true
console.log(myInstanceof(f, Function)); // false ❗
console.log(myInstanceof(F, Function)); // true
总结
核心要点
-
函数有双重身份:既是对象(继承自Function.prototype),又是构造函数(为实例提供F.prototype)
-
原型链路径不同:
- 函数:
F → Function.prototype → Object.prototype → null - 实例:
f → F.prototype → Object.prototype → null
- 函数:
-
关键区别:
F.prototype≠Function.prototype
记忆口诀
函数是对象,走Function链
实例找原型,走构造函数链
Function.prototype,只有函数能访问
F.prototype,实例的专属通道
实践建议
- 调试时:使用
Object.getPrototypeOf()而不是__proto__ - 扩展时:区分是扩展函数能力还是实例能力
- 检测时:理解
instanceof的真正含义 - 性能考量:原型链越长,查找越慢
如果您觉得这篇文章有帮助,请点个赞吧~
相关文章
更多文章 →八股文2025-10-13
cookie跨域介绍
Cookie 跨域问题详解 在 Web 开发中, Cookie 是最常见的客户端存储机制之一,用于记录用户登录状态、偏好设置、会话信息等。然而,当涉及到 跨域请求 时,Cookie 的行为往往变得复杂,尤其在现代浏览器的安全策略下,跨域 Cookie 的传递和写入都有严格的限制。 本文将从基础概念开始,逐步讲解 Cookie 的跨域机制、SameSite 属性、CORS 配置及常见问题解决方案。 一、Cookie 的基础概念 Cooki...
学习面试
八股文2025-10-09
JavaScript 闭包详解
JavaScript 闭包(Closure)详解 一、什么是闭包 闭包(Closure) 是 JavaScript 中一个非常核心且常被问到的概念。简单来说: 闭包是一个函数,它可以“记住”并访问其定义时所在的词法作用域,即使这个函数在其作用域之外被调用。 换句话说,当一个函数“嵌套”在另一个函数中,并且 内部函数引用了外部函数的变量 时,就形成了闭包。 示例: 虽然 已经执行完毕,但 依然可以访问 中的 。这就是闭包。 二、闭包的形成...
学习面试
八股文2025-10-07
开始性能优化之旅
事件循环机制 一、JavaScript 引擎的本质 核心职责 : 解析 JavaScript 语法 管理变量和内存 执行代码逻辑 不涉及 : 线程管理(Worker除外) I/O 操作 定时器控制 网络请求 常见引擎:V8(Chrome)、SpiderMonkey(Firefox)、JavaScriptCore(Safari) 二、宿主环境的扩展能力 宿主提供的多线程能力 : | 线程类型 | 功能 | 对应 API | | | | |...
学习面试
八股文2025-09-30
前端首屏优化
话说,我在面试的时候,80%的情况下,都会被问到首屏优化问题,烦,恨 TCP Slow Start(慢启动)概念 咱先不说标题的数字哪里来的,先说一个概念,就是TCP的慢启动。 你们想哈,在浏览器和服务器开始建立连接的时候,服务器是并不知道浏览端网络的带宽、拥塞状况。假设你本地的带宽是1M,如果一开始服务器就发送2M的文件,那浏览器压根就不是人类,忍忍或者挤挤就能接收到的,这样就可能会引发丢包和重传。 想想,这个该有什么办法解决这个问题...
学习面试
八股文2025-09-10
让你彻底明白什么是闭包
今天我们来聊一个听起来很高大上,但实际上你可能天天在用(只是不知道它名字)的概念—— 闭包 。 一、一个你肯定写过的闭包 先别管定义,来看这段代码,你是不是再熟悉不过了? 恭喜你!这就是一个经典的闭包!是不是很简单? 二、为什么会有闭包?—— 背包的故事 想象一下,JavaScript 中的函数就像一个小机器人,当它被创建时,会背着一个神奇的 背包 。 这个背包里装着什么呢?装着它 出生时 所在环境的所有变量! 当我们调用 时,返回的那...
学习面试
八股文2025-09-01
JavaScript 原型链深度解析
JavaScript 原型链深度解析:从概念到实践 前言 JavaScript 原型链是前端开发中最重要也是最容易混淆的概念之一。理解原型链不仅有助于我们掌握 JavaScript 的面向对象编程,更是深入理解继承、方法查找等核心机制的关键。本文将从基础概念开始,逐步深入到原型链的实际应用。 1\. 核心概念理解 什么是原型? 在 JavaScript 中,每个对象都有一个内部属性指向另一个对象,这个被指向的对象就是原型。原型本身也是一...
学习面试
评论
请登录后发表评论
去登录