阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 原型与原型链

原型与原型链

作者:陈川 阅读数:45083人阅读 分类: JavaScript

原型

JavaScript中的每个对象都有一个原型(prototype),原型本身也是一个对象。对象会继承原型的属性和方法。原型是JavaScript实现继承的基础。构造函数通过prototype属性指向原型对象,实例对象通过__proto__属性访问其原型。

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

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

const person1 = new Person('Alice');
person1.sayHello(); // 输出: Hello, my name is Alice

在这个例子中,Person是一个构造函数,它的prototype属性指向原型对象。当我们创建person1实例时,实例通过__proto__链接到Person.prototype,因此可以访问sayHello方法。

原型链

原型链是JavaScript实现继承的机制。当访问一个对象的属性时,如果对象本身没有该属性,JavaScript会沿着原型链向上查找,直到找到该属性或到达原型链的末端(null)。这种链式结构就是原型链。

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(`${this.name} is barking!`);
};

const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.eat(); // 输出: Buddy is eating.
myDog.bark(); // 输出: Buddy is barking!

这里Dog继承了Animal的特性。Dog.prototype的原型是Animal.prototype,形成了原型链。当调用myDog.eat()时,JavaScript先在myDog实例上查找,没找到就沿着原型链在Dog.prototype上查找,最后在Animal.prototype上找到。

原型相关方法

JavaScript提供了几个与原型相关的重要方法:

  1. Object.getPrototypeOf(obj):获取对象的原型
  2. Object.setPrototypeOf(obj, prototype):设置对象的原型
  3. obj.hasOwnProperty(prop):检查属性是否是对象自身的(非继承的)
  4. prop in obj:检查对象或其原型链上是否存在属性
const obj = {};
const parent = { x: 1 };

Object.setPrototypeOf(obj, parent);

console.log(Object.getPrototypeOf(obj) === parent); // true
console.log(obj.x); // 1 (继承自parent)
console.log(obj.hasOwnProperty('x')); // false
console.log('x' in obj); // true

构造函数与原型

构造函数、原型和实例之间的关系是理解原型链的关键。每个构造函数都有一个prototype属性指向原型对象,原型对象有一个constructor属性指回构造函数,实例通过__proto__链接到原型对象。

function Car(make, model) {
  this.make = make;
  this.model = model;
}

Car.prototype.displayInfo = function() {
  console.log(`This is a ${this.make} ${this.model}`);
};

const myCar = new Car('Toyota', 'Camry');

console.log(Car.prototype.constructor === Car); // true
console.log(myCar.__proto__ === Car.prototype); // true
console.log(myCar instanceof Car); // true

原型继承的优缺点

原型继承有其独特的优势和局限性:

优点:

  • 内存效率高,共享方法只在原型上定义一次
  • 动态性,修改原型会立即影响所有实例
  • 实现简单,不需要类语法也能实现继承

缺点:

  • 共享引用类型属性可能导致问题
  • 子类型无法向超类型传递参数
  • 原型链过长会影响性能
function Parent() {
  this.colors = ['red', 'blue'];
}

function Child() {}

Child.prototype = new Parent();

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

const child2 = new Child();
console.log(child2.colors); // ['red', 'blue', 'green'] (共享了colors属性)

ES6类与原型

ES6引入了class语法,但底层仍然是基于原型的继承。class只是语法糖,让原型继承更易理解和使用。

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() {
    console.log(`${this.name} barks!`);
  }
}

const d = new Dog('Mitzie', 'Poodle');
d.speak(); // Mitzie barks!

使用Babel等工具转换后,可以看到class实际上还是转换为原型继承的代码。

原型污染与防御

原型污染是指恶意修改Object.prototype等基础原型,导致所有对象都受到影响。这是一种常见的安全问题。

// 恶意代码
Object.prototype.isAdmin = true;

// 受影响代码
const user = { name: 'Alice' };
if (user.isAdmin) {
  // 本不该执行这里的代码
  console.log('You have admin privileges!');
}

防御原型污染的方法:

  1. 使用Object.create(null)创建无原型的对象
  2. 使用Object.freeze()冻结原型
  3. 使用hasOwnProperty检查属性
// 安全做法
const safeObj = Object.create(null);
safeObj.name = 'Safe Object';
console.log(safeObj.hasOwnProperty); // undefined

// 冻结原型
Object.freeze(Object.prototype);
Object.prototype.isAdmin = true; // 在严格模式下会报错

性能考虑

原型链查找会影响性能,过长的原型链会导致属性查找时间增加。现代JavaScript引擎会优化原型查找,但仍需注意:

  1. 避免过深的原型链继承
  2. 频繁访问的属性可以直接定义在对象上
  3. 使用组合而非继承来共享代码
// 性能较差的深继承链
function A() {}
function B() {}
function C() {}
function D() {}

B.prototype = new A();
C.prototype = new B();
D.prototype = new C();

const obj = new D();
// 查找属性需要遍历4层原型链

// 更好的方式:组合
const features = {
  feature1() {},
  feature2() {}
};

function MyObject() {
  Object.assign(this, features);
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:对象枚举与迭代

下一篇:constructor属性

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌