程序员面试宝典

一站式面试准备平台

返回分类
JavaScript中级

JavaScript 原型与原型链

深入理解 JavaScript 原型、原型链及继承机制

2026-03-28
阅读时间: 12分钟

JavaScript 原型与原型链

原型是 JavaScript 最核心的概念之一,理解原型是掌握 JavaScript 面向对象编程的关键。

原型基础

prototype 属性

每个函数(除箭头函数)都有一个 prototype 属性,它是一个对象:

javascript
function Person(name, age) {
    this.name = name;
    this.age = age;
}

console.log(Person.prototype); // { constructor: Person }

constructor 属性

prototype 对象上有一个 constructor 属性,指向构造函数本身:

javascript
function Person(name, age) {
    this.name = name;
    this.age = age;
}

console.log(Person.prototype.constructor === Person); // true

proto 与原型对象

对象的隐式原型

每个对象都有一个 __proto__ 属性(部分环境为 [[Prototype]]),指向其构造函数的 prototype:

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

const person = new Person('张三');

console.log(person.__proto__ === Person.prototype); // true

原型链查找

访问对象的属性时,会沿着原型链向上查找:

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

Person.prototype.greet = function() {
    return `你好,我是${this.name}`;
};

const person = new Person('张三');

console.log(person.name);        // '张三'(自有属性)
console.log(person.greet());     // '你好,我是张三'(原型链查找)
console.log(person.toString());  // '[object Object]'(Object.prototype)

原型链

原型链的结构

person 实例
    │
    ├─ name: '张三'
    │
    └─ __proto__ ──────────► Person.prototype
                                │
                                ├─ constructor: Person
                                │
                                ├─ greet: function
                                │
                                └─ __proto__ ──────► Object.prototype
                                                      │
                                                      ├─ constructor: Object
                                                      │
                                                      ├─ toString: function
                                                      │
                                                      ├─ hasOwnProperty: function
                                                      │
                                                      └─ __proto__ ──────► null(原型链终点)

原型链的尽头

Object.prototype 的 __proto__ 为 null,这就是原型链的尽头:

javascript
console.log(Object.prototype.__proto__); // null

属性遮蔽

原型链上的属性会被自有属性遮蔽:

javascript
function Person() {}

Person.prototype.name = '原型名称';

const person = new Person();
console.log(person.name); // '原型名称'

person.name = '自有名称';
console.log(person.name); // '自有名称'(遮蔽了原型属性)
console.log(person.__proto__.name); // '原型名称'(原型属性未被修改)

基于原型的继承

原型链继承

javascript
function Animal(name) {
    this.name = name;
}

Animal.prototype.eat = function() {
    return `${this.name} 正在吃东西`;
};

function Dog(name, breed) {
    Animal.call(this, name);  // 调用父构造函数
    this.breed = breed;
}

// 原型链继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
    return `${this.name} 正在叫`;
};

const dog = new Dog('旺财', '金毛');
console.log(dog.eat());  // '旺财 正在吃东西'
console.log(dog.bark()); // '旺财 正在叫'

ES6 class 与原型

ES6 的 class 语法只是原型链继承的语法糖:

javascript
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    eat() {
        return `${this.name} 正在吃东西`;
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);  // 调用父类构造函数
        this.breed = breed;
    }
    
    bark() {
        return `${this.name} 正在叫`;
    }
}

const dog = new Dog('旺财', '金毛');
console.log(dog instanceof Animal); // true
console.log(dog instanceof Dog);    // true

原型链与属性操作

hasOwnProperty

判断属性是否为自有属性:

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

Person.prototype.age = 25;

const person = new Person('张三');

console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('age'));  // false(原型属性)
console.log(person.__proto__.hasOwnProperty('age')); // true

in 操作符

in 操作符会检查原型链:

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

Person.prototype.age = 25;

const person = new Person('张三');

console.log('name' in person); // true(自有)
console.log('age' in person);  // true(原型链)
console.log('toString' in person); // true(Object.prototype)

for...in 遍历

for...in 遍历包括原型链上的可枚举属性:

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

Person.prototype.age = 25;

const person = new Person('张三');

for (const key in person) {
    if (person.hasOwnProperty(key)) {
        console.log(key, person[key]); // name - 张三
    }
}

常见面试问题

Q1: 原型链继承和构造函数继承的区别?

javascript
// 原型链继承:共享方法,节省内存
// 构造函数继承:每个实例有独立的方法

function Parent() {
    this.colors = ['红', '绿'];
}

function Child() {
    Parent.call(this); // 构造函数继承
}

// 如果不加这行,原型链继承
Child.prototype = new Parent(); // 问题:共享引用类型的原型属性

Q2: 为什么函数有 prototype 而对象没有?

  • 函数有 prototype 属性:用于创建实例的原型对象
  • 对象有 __proto__ 属性:指向其构造函数的 prototype
javascript
const obj = {};
console.log(obj.prototype); // undefined(对象没有 prototype)
console.log(obj.__proto__); // Object.prototype(对象有 __proto__)

Q3: instanceof 的原理?

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

const person = new Person('张三');

// instanceof 检查 Person.prototype 是否在 person 的原型链中
console.log(person instanceof Person);      // true
console.log(person instanceof Object);     // true
console.log(person instanceof Array);      // false

Q4: 如何创建一个没有原型的对象?

javascript
const obj = Object.create(null);
console.log(obj.__proto__); // undefined
console.log(obj.toString);   // undefined

// 纯字典对象,常用于 Map 或性能敏感场景

最佳实践

  1. 优先使用 ES6 class:语法更清晰,行为更可预测
  2. 使用 Object.create() 创建原型继承:更灵活的原型继承方式
  3. 避免修改 Object.prototype:会影响所有对象
  4. 使用 hasOwnProperty 检查自有属性:避免原型链干扰
  5. 原型链不要太长:超过 3-4 层会影响性能

相关标签