Skip to content

JavaScript 原型与原型链深度解析

原型系统核心概念

原型链结构示意图

原型定义

  • 函数对象:所有函数都有 prototype 属性(显式原型)
  • 实例对象:所有对象都有 __proto__ 属性(隐式原型),指向其构造函数的 prototype
  • 特殊关系
    • 函数的实例对象没有 prototype 属性
    • Object.prototype 是所有对象的原型终点

原型链形成机制

  1. 对象访问属性时,先在自身属性中查找
  2. 若未找到,通过 __proto__ 向上层原型查找
  3. 递归执行此过程,直到找到属性或到达原型链顶端(null
javascript
function Person() {}
Person.prototype.say = function () {
  return "Hello";
};

const person = new Person();
console.log(person.say()); // "Hello"(来自原型)

关键原型指向关系

表达式说明
Object.prototype.__proto__null原型链终点
Function.prototype === Function.__proto__true特殊自引用关系
Object.__proto__ === Function.prototypetrueObject 是 Function 的实例
Array.__proto__ === Function.prototypetrue数组构造函数继承自 Function

对象与函数创建原理

对象创建

javascript
// 字面量创建(推荐)
const obj1 = {};

// 构造函数创建
const obj2 = new Object();

// 原型关系验证
console.log(obj1.__proto__ === Object.prototype); // true
console.log(obj2.__proto__ === Object.prototype); // true

函数创建

javascript
// 函数声明
function fn() {}

// 构造函数创建
const fn2 = new Function();

// 原型关系验证
console.log(fn.__proto__ === Function.prototype); // true
console.log(fn2.__proto__ === Function.prototype); // true

constructor 属性

核心特性

  • 实例对象本身没有 constructor 属性
  • 访问时从原型链查找,最终指向构造函数
javascript
function Person() {}
const p = new Person();

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

// 内置对象验证
console.log([].constructor === Array); // true
console.log({}.constructor === Object); // true

原型链核心优势

  1. 共享数据:减少内存占用

    javascript
    function Person() {}
    Person.prototype.species = "Human";
    
    const p1 = new Person();
    const p2 = new Person();
    // 共享species属性,不占用实例内存
  2. 实现继承:建立对象间继承关系

类型检测机制

instanceof 原理

  • 检测左操作数是否在右操作数的原型链上
  • 递归检查 left.__proto__ === right.prototype
javascript
function Animal() {}
function Dog() {}
Dog.prototype = new Animal();

const dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true

安全检测问题

javascript
// 未声明变量检测
typeof undeclaredVar; // 'undefined'(安全)
undeclaredVar instanceof Object; // ReferenceError(危险)

实现 instanceof

javascript
function myInstanceof(left, right) {
  // 基本类型直接返回false
  if (left === null || typeof left !== "object") return false;

  let proto = left.__proto__;
  const prototype = right.prototype;

  while (true) {
    if (proto === null) return false;
    if (proto === prototype) return true;
    proto = proto.__proto__;
  }
}

原型属性检测

hasOwnProperty

  • 检测属性是否属于对象自身(非原型链)
  • 不查找原型链
javascript
const obj = { name: "John" };
console.log(obj.hasOwnProperty("name")); // true
console.log(obj.hasOwnProperty("toString")); // false(来自原型)

核心方法实现

new 操作符实现

javascript
function myNew(Constructor, ...args) {
  // 1. 创建新对象并链接原型
  const obj = Object.create(Constructor.prototype);

  // 2. 执行构造函数并绑定this
  const result = Constructor.apply(obj, args);

  // 3. 处理返回值
  return result instanceof Object ? result : obj;
}

// 使用示例
function Person(name) {
  this.name = name;
}
const p = myNew(Person, "Alice");
console.log(p.name); // 'Alice'

Object.create 实现

javascript
Object.myCreate = function (proto) {
  function F() {} // 中介构造函数
  F.prototype = proto; // 设置原型
  return new F(); // 返回实例
};

// 使用示例
const base = { type: "object" };
const obj = Object.myCreate(base);
console.log(obj.type); // 'object'
console.log(obj.__proto__ === base); // true

继承实现方案

1. 构造继承(经典继承)

特点

  • 继承父类实例属性
  • 无法继承原型方法
javascript
function Parent(name) {
  this.name = name;
  this.colors = ["red"];
}

function Child(name) {
  Parent.call(this, name); // 调用父类构造函数
}

const c1 = new Child("Tom");
c1.colors.push("blue");
console.log(c1.colors); // ['red', 'blue']

const c2 = new Child("Jerry");
console.log(c2.colors); // ['red'](独立实例)

2. 原型继承

特点

  • 共享原型属性
  • 实例间相互影响
javascript
function Parent() {
  this.nums = [1, 2];
}

function Child() {}

Child.prototype = new Parent(); // 设置原型

const c1 = new Child();
c1.nums.push(3);

const c2 = new Child();
console.log(c2.nums); // [1, 2, 3](共享问题)

3. 组合继承(推荐)

特点

  • 结合构造和原型继承优点
  • 实例属性独立,原型方法共享
javascript
function Parent(name) {
  this.name = name;
  this.colors = ["red"];
}

Parent.prototype.sayName = function () {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name); // 继承实例属性
  this.age = age;
}

// 继承原型方法
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // 修复constructor

const c1 = new Child("Tom", 10);
c1.colors.push("blue");

const c2 = new Child("Jerry", 8);
console.log(c1.colors); // ['red', 'blue']
console.log(c2.colors); // ['red'](独立)
c1.sayName(); // 'Tom'(共享方法)

4. 多继承实现

javascript
function extend(target, ...sources) {
  sources.forEach((source) => {
    // 复制实例属性
    Object.getOwnPropertyNames(source.prototype).forEach((prop) => {
      if (prop !== "constructor") {
        target.prototype[prop] = source.prototype[prop];
      }
    });
  });
  return target;
}

// 使用示例
function Flyer() {}
Flyer.prototype.fly = function () {
  console.log("Flying");
};

function Swimmer() {}
Swimmer.prototype.swim = function () {
  console.log("Swimming");
};

function SuperHero() {}
extend(SuperHero, Flyer, Swimmer);

const hero = new SuperHero();
hero.fly(); // 'Flying'
hero.swim(); // 'Swimming'

最佳实践建议

  1. 优先使用组合继承:平衡性能和功能需求
  2. 避免修改内置原型:防止命名冲突
  3. 使用Object.create设置原型:比直接赋值更安全
  4. 始终修复 constructor:保持正确的构造函数指向
  5. 现代替代方案:考虑 ES6 的classextends语法