目录

六十天前端强化训练之第十三天之JavaScript-原型与继承详解

六十天前端强化训练之第十三天之JavaScript 原型与继承详解

=====欢迎来到编程星辰海的博客讲解======


一、原型系统底层探秘

1.1 对象体系构建原理

JavaScript 的对象系统基于三大核心要素:

  • 构造函数 :用于创建对象的函数模板(本质仍是函数)
  • 原型对象 :包含共享属性和方法的对象
  • 实例对象 :通过 new 操作符创建的具体对象
内存模型示意图

BASH

实例对象 (obj)
  ├── [[Prototype]] --> 原型对象 (Constructor.prototype)
  │       ├── constructor --> 构造函数 (Constructor)
  │       └── [[Prototype]] --> Object.prototype
  └── 私有属性

1.2 原型链工作机制

属性访问过程详解

  1. 在实例自身属性中查找
  2. 未找到则向上查找原型对象
  3. 递归直到 Object.prototype
  4. Object.prototype 的 [[Prototype]] 为 null

JAVASCRIPT

const animal = { eats: true };
const rabbit = { jumps: true, __proto__: animal };

console.log(rabbit.eats); // true(原型链查找)
console.log(rabbit.jumps); // true(自有属性)

1.3 原型相关方法详解

(1) Object.getPrototypeOf

JAVASCRIPT

function Person() {}
const p = new Person();
console.log(Object.getPrototypeOf(p) === Person.prototype); // true
(2) Object.setPrototypeOf

JAVASCRIPT

const basic = { version: 1.0 };
const extended = {};
Object.setPrototypeOf(extended, basic);
console.log(extended.version); // 1.0
(3) Object.create

JAVASCRIPT

const protoObj = { log() { console.log(this.msg) } };
const child = Object.create(protoObj, {
  msg: { value: 'Hello World' }
});
child.log(); // "Hello World"

1.4 构造函数运行机制

new 操作符完整执行流程:

  1. 创建空对象 const obj = {}
  2. 绑定原型 obj.__proto__ = Constructor.prototype
  3. 执行构造函数 Constructor.call(obj, args)
  4. 返回对象(如果构造函数返回非对象,则返回 obj)

手动实现 new 操作符

JAVASCRIPT

function myNew(Con, ...args) {
  const obj = Object.create(Con.prototype);
  const result = Con.apply(obj, args);
  return result instanceof Object ? result : obj;
}

二、八种继承模式深度剖析

2.1 原型链继承(经典方式)

JAVASCRIPT

function Parent() {
  this.parentProp = true;
}
Parent.prototype.getParentProp = function() {
  return this.parentProp;
};

function Child() {
  this.childProp = false;
}

// 实现继承
Child.prototype = new Parent();

const instance = new Child();
console.log(instance.getParentProp()); // true

存在问题

  • 引用类型共享问题
  • 无法向父类传参

2.2 构造函数继承

JAVASCRIPT

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

function Child(name) {
  Parent.call(this, name); // 关键代码
}

const child1 = new Child('Tom');
child1.colors.push('green');

const child2 = new Child('Jerry');
console.log(child2.colors); // ['red', 'blue'](独立副本)

优势与局限

  • ✔ 解决属性共享问题
  • ✖ 无法继承原型方法

2.3 组合继承(伪经典继承)

JAVASCRIPT

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

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

function Child(name, age) {
  Parent.call(this, name); // 第二次调用
  this.age = age;
}

Child.prototype = new Parent(); // 第一次调用
Child.prototype.constructor = Child;

const child = new Child('Lucy', 12);
child.sayName(); // "Lucy"

效率问题

父构造函数被调用两次导致属性重复(实例属性和原型属性)

2.4 原型式继承

JAVASCRIPT

const person = {
  name: 'Default',
  friends: ['Alice', 'Bob']
};

const another = Object.create(person, {
  name: { value: 'Greg' }
});

another.friends.push('Charlie');
console.log(person.friends); // ['Alice', 'Bob', 'Charlie']

适用场景

不需要构造函数的简单对象继承

2.5 寄生式继承

JAVASCRIPT

function createEnhance(obj) {
  const clone = Object.create(obj);
  clone.sayHi = function() {
    console.log('Hi!');
  };
  return clone;
}

const base = { name: 'Base' };
const enhanced = createEnhance(base);
enhanced.sayHi(); // "Hi!"

特点

  • 为对象添加额外方法
  • 无法实现函数复用

2.6 寄生组合继承(终极方案)

JAVASCRIPT

function inheritPrototype(child, parent) {
  const prototype = Object.create(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}

function Parent(name) {
  this.name = name;
}

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

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

inheritPrototype(Child, Parent);

const child = new Child('Sam', 10);
child.sayName(); // "Sam"

优势分析

  • 只调用一次父构造函数
  • 原型链保持正确
  • 无属性冗余

2.7 ES6类继承

JAVASCRIPT

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    super.speak();
    console.log(`${this.name} barks.`);
  }
}

const d = new Dog('Buddy', 'Golden Retriever');
d.speak();
// "Buddy makes a noise."
// "Buddy barks."

2.8 多重继承实现

JAVASCRIPT

function mix(...mixins) {
  class Mix {
    constructor() {
      for (let mixin of mixins) {
        copyProperties(this, new mixin());
      }
    }
  }

  function copyProperties(target, source) {
    for (let key of Reflect.ownKeys(source)) {
      if (key !== 'constructor') {
        Object.defineProperty(target, key, 
          Object.getOwnPropertyDescriptor(source, key));
      }
    }
  }

  return Mix;
}

class A { methodA() {} }
class B { methodB() {} }

class C extends mix(A, B) {}

三、原型操作最佳实践

3.1 原型污染防护

JAVASCRIPT

// 安全的对象创建
const safeObj = Object.create(null);
safeObj.toString = function() { /* custom */ };

// 冻结原型
Object.freeze(Object.prototype);

3.2 原型方法扩展规范

JAVASCRIPT

// 正确的方式
if (!Array.prototype.customMethod) {
  Array.prototype.customMethod = function() {
    // 实现代码
  };
}

// 错误示例(可能引发问题)
Array.prototype = {
  newMethod: function() { /*...*/ }
};

3.3 原型调试技巧

使用 Chrome DevTools 检查原型链:

JAVASCRIPT

function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype);

const obj = new Child();

// 控制台输入
console.log(obj);
// 展开 __proto__ 链查看继承关系

// 验证继承关系
console.log(obj instanceof Child);  // true
console.log(obj instanceof Parent); // true

四、实战案例:完整电商系统类设计

4.1 类结构设计

JAVASCRIPT

class Product {
  constructor(id, name, price) {
    this.id = id;
    this.name = name;
    this.price = price;
  }

  display() {
    console.log(`Product: ${this.name} ($${this.price})`);
  }
}

class DigitalProduct extends Product {
  constructor(id, name, price, fileSize) {
    super(id, name, price);
    this.fileSize = fileSize;
  }

  download() {
    console.log(`Downloading ${this.name} (${this.fileSize}MB)`);
  }
}

class ShoppingCart {
  constructor() {
    this.items = [];
  }

  addItem(product, quantity = 1) {
    this.items.push({ product, quantity });
  }

  calculateTotal() {
    return this.items.reduce((sum, item) => 
      sum + item.product.price * item.quantity, 0);
  }
}

// 使用示例
const book = new Product(1, 'JS Guide', 39.99);
const course = new DigitalProduct(2, 'React Course', 99, 2048);

const cart = new ShoppingCart();
cart.addItem(book, 2);
cart.addItem(course);

console.log(cart.calculateTotal()); // 39.99*2 + 99 = 178.98

4.2 原型关系图

TEXT

ShoppingCart.prototype --> Object.prototype
DigitalProduct.prototype --> Product.prototype --> Object.prototype

五、扩展阅读推荐

5.1 必读文档

5.2 经典图书

  • 《JavaScript 设计模式与开发实践》第三章
  • 《编写可维护的JavaScript》第6章
  • 《JavaScript 悟道》第9章 对象系统

5.3 视频资源

5.4 在线实验


以上内容通过 Chrome 95+ 浏览器验证测试,所有代码示例均可直接复制到开发者工具控制台运行。建议配合浏览器调试工具逐步跟踪原型链变化,可加深理解。