原型&原型链 梳理

56 阅读4分钟

🎯 面试标准回答(推荐结构)

1️⃣ 概念定义(30秒)

"原型是 JavaScript 实现继承的机制。每个 JavaScript 对象都有一个内部属性 [[Prototype]](可通过 __proto__ 访问),它指向该对象的原型对象。当我们访问对象的属性或方法时,如果对象本身没有,JavaScript 会沿着原型链向上查找,直到找到该属性或到达原型链的顶端 null。"

2️⃣ 举例说明(1分钟)

let obj = { age: 25 }

// obj 可以使用很多方法,比如:
obj.toString()
obj.hasOwnProperty('age')

// 这些方法并不是 obj 自己定义的
// 而是通过 __proto__ 从原型对象上继承来的
console.log(obj.__proto__ === Object.prototype) // true

3️⃣ 核心关系(1分钟)

面试官可能追问,这时你可以说明三个关键关系:

function Person(name) {
  this.name = name
}

let person = new Person('张三')

// 三角关系:
// 1. 实例的 __proto__ 指向构造函数的 prototype
person.__proto__ === Person.prototype // true

// 2. 原型的 constructor 指向构造函数
Person.prototype.constructor === Person // true

// 3. 构造函数的 prototype 是一个对象
typeof Person.prototype === 'object' // true

4️⃣ 原型链(30秒-1分钟)

// 原型链:对象通过 __proto__ 连接起来的链条
person.__proto__ === Person.prototype
Person.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null  // 原型链的终点

"当访问 person.toString() 时,查找过程是:

  1. 先看 person 对象自身有没有
  2. 没有就去 Person.prototype 找
  3. 还没有就去 Object.prototype 找
  4. 再没有就返回 undefined"

5️⃣ 加分项(如果时间允许)

可以补充的高级知识点:

  1. 性能优化

    // 方法定义在原型上,所有实例共享,节省内存
    Person.prototype.sayHello = function() {
      console.log('Hello, ' + this.name)
    }
    
  2. 原型污染风险

    // 修改原型会影响所有实例
    Object.prototype.hack = 'danger'
    console.log({}.hack) // 'danger' - 所有对象都被污染
    
  3. 现代替代方案

    // ES6 后推荐使用 Object.getPrototypeOf() 和 Object.setPrototypeOf()
    // 而不是直接操作 __proto__
    Object.getPrototypeOf(obj) === Object.prototype
    

📝 面试回答模板(完整版)

【第1层 - 基础定义】
"原型是 JavaScript 的继承机制。每个对象都有一个内部属性指向它的原型,
我们可以通过 __proto__ 访问。原型本身也是一个对象。"

【第2层 - 作用说明】
"原型的作用是实现属性和方法的共享。比如我创建一个对象 let obj = {},
虽然我没有定义 toString 方法,但 obj 可以调用 obj.toString(),
这是因为它从 Object.prototype 上继承了这个方法。"

【第3层 - 原型链】
"多个对象通过 __proto__ 连接起来就形成了原型链。JavaScript 查找属性时,
会沿着原型链向上查找,直到找到该属性或到达 null 为止。"

【第4层 - 构造函数关系】
"对于构造函数来说,有三个关键关系:
1. 实例的 __proto__ 指向构造函数的 prototype
2. 构造函数的 prototype.constructor 指回构造函数本身
3. 所有函数的 __proto__ 最终指向 Function.prototype"

【第5层 - 实际应用】
"在实际开发中,我们通常把共享的方法定义在原型上,这样所有实例都能访问,
同时节省内存。ES6 的 class 语法本质上也是基于原型的语法糖。"

🔥 常见追问及回答

Q1: "原型和原型链的区别?"

A: "原型是单个对象,原型链是多个原型对象通过 __proto__ 连接形成的链条。原型解决的是属性共享,原型链解决的是属性查找。"

Q2: "__proto__prototype 有什么区别?"

A:

  • __proto__每个对象都有的属性,指向它的原型
  • prototype函数才有的属性,指向通过该函数创建的实例的原型
  • 实例的 __proto__ === 构造函数的 prototype

Q3: "如何判断属性是对象自己的还是原型上的?"

A:

obj.hasOwnProperty('age') // 判断是否是自有属性
'age' in obj // 判断对象及其原型链上是否有该属性

Q4: "原型链的顶端是什么?"

A: "Object.prototype.__proto__null,这是原型链的终点。"


💡 面试技巧

  1. 先答概念,再举例子:不要一上来就讲细节
  2. 画图辅助:如果是白板面试,画出原型链关系图会大加分
  3. 联系实际:提到 ES6 class、继承、性能优化等实际应用
  4. 层次清晰:从简单到复杂,根据面试官反应调整深度
  5. 准备代码:最好能手写一个原型链的例子

记住:面试不是背书,而是展示你的理解深度