您现在的位置是:网站首页 > 原型与原型链文章详情

原型与原型链

在JavaScript中,理解原型与原型链是深入掌握对象继承机制的核心。对象的属性和方法通过原型链动态查找,这种设计既灵活又高效。

原型的概念

每个JavaScript对象(除null)都有一个隐藏属性[[Prototype]],指向它的原型对象。通过__proto__(非标准)或Object.getPrototypeOf()可访问它。构造函数通过prototype属性为实例提供共享原型:

function Person(name) {
  this.name = name;
}
Person.prototype.sayHello = function() {
  console.log(`Hello, ${this.name}!`);
};

const alice = new Person('Alice');
alice.sayHello(); // 输出: Hello, Alice!

这里Person.prototype成为alice的原型,sayHello方法被所有实例共享。

原型链的运作机制

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

console.log(alice.toString()); // 输出: [object Object]

尽管alice没有toString方法,但通过原型链找到Object.prototype.toString。完整的链条如下:

alice → Person.prototype → Object.prototype → null

手动修改原型链的例子:

const parent = { familyName: 'Smith' };
const child = Object.create(parent);
console.log(child.familyName); // 输出: Smith

此时原型链为:

child → parent → Object.prototype → null

构造函数与原型的关系

构造函数通过new操作符创建实例时,实例的[[Prototype]]会指向构造函数的prototype属性:

function Car(model) {
  this.model = model;
}
Car.prototype.getModel = function() {
  return this.model;
};

const tesla = new Car('Model S');
console.log(Object.getPrototypeOf(tesla) === Car.prototype); // true

原型继承的实现

实现继承需要连接子类和父类的原型链:

function Animal(name) {
  this.name = name;
}
Animal.prototype.eat = function() {
  console.log(`${this.name} is eating.`);
};

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() {
  console.log('Woof!');
};

const myDog = new Dog('Rex', 'Labrador');
myDog.eat(); // 输出: Rex is eating.

现代语法中的原型

ES6的class本质仍是原型继承的语法糖:

class Shape {
  constructor(color) {
    this.color = color;
  }
  draw() {
    console.log(`Drawing a ${this.color} shape.`);
  }
}

class Circle extends Shape {
  constructor(color, radius) {
    super(color);
    this.radius = radius;
  }
  getArea() {
    return Math.PI * this.radius ** 2;
  }
}

const redCircle = new Circle('red', 5);
redCircle.draw(); // 输出: Drawing a red shape.

原型链的边界情况

原型链的终点是null,尝试越过终点访问属性会报错:

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

手动构建多层原型链:

const level1 = { a: 1 };
const level2 = Object.create(level1);
level2.b = 2;
const level3 = Object.create(level2);
level3.c = 3;

console.log(level3.a); // 1 (来自level1)
console.log(level3.b); // 2 (来自level2)

性能与内存考量

原型链过长会影响属性查找速度。V8引擎会优化重复查找,但深度超过10层的原型链仍可能降低性能:

// 创建深度原型链
let current = {};
for (let i = 0; i < 20; i++) {
  current = Object.create(current);
  current[`prop${i}`] = i;
}
console.time('deep lookup');
console.log(current.prop19);
console.timeEnd('deep lookup'); // 比浅层查找耗时更长

原型污染与防御

直接修改内置原型可能引发意外行为:

Array.prototype.push = function() {
  console.log('Array.push disabled!');
};
const arr = [1, 2];
arr.push(3); // 输出: Array.push disabled!

安全的原型扩展方式:

// 使用Symbol作为属性键避免冲突
const customMethod = Symbol('custom');
Array.prototype[customMethod] = function() {
  console.log('Safe extension');
};
[][customMethod](); // 输出: Safe extension

原型相关的API

Object方法提供了原型操作能力:

const proto = { x: 10 };
const obj = Object.create(proto, {
  y: { value: 20, enumerable: true }
});

console.log(Object.getPrototypeOf(obj) === proto); // true
console.log(Object.getOwnPropertyNames(obj)); // ["y"]

// 修改原型
Object.setPrototypeOf(obj, { z: 30 });
console.log(obj.z); // 30

实际应用场景

利用原型实现插件系统:

function Core() {}
Core.prototype.plugins = [];

Core.prototype.use = function(plugin) {
  this.plugins.push(plugin);
  plugin(this);
};

function LoggerPlugin(core) {
  core.log = function(message) {
    console.log(`[LOG] ${message}`);
  };
}

const app = new Core();
app.use(LoggerPlugin);
app.log('System started'); // 输出: [LOG] System started

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步