跳转到内容

JavaScript原型

来自代码酷

JavaScript原型[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

在JavaScript中,原型(Prototype)是对象继承机制的核心概念。每个JavaScript对象都有一个内部属性Prototype(可通过__proto__访问),它指向另一个对象或null。原型链允许对象继承其他对象的属性和方法,从而实现代码复用和面向对象编程。

JavaScript的函数对象还有一个特殊的属性prototype,它仅在函数作为构造函数(通过new调用)时生效,用于设置新创建对象的原型。

原型链[编辑 | 编辑源代码]

当访问一个对象的属性时,JavaScript引擎会先检查对象自身是否有该属性。如果没有,则沿着Prototype链向上查找,直到找到该属性或到达原型链末端(null)。

graph LR A[实例对象] -->|__proto__| B[构造函数的prototype] B -->|__proto__| C[Object.prototype] C -->|__proto__| D[null]

构造函数与prototype[编辑 | 编辑源代码]

当使用构造函数创建对象时,新对象的Prototype会指向构造函数的prototype属性。

// 构造函数
function Person(name) {
    this.name = name;
}

// 在构造函数的prototype上添加方法
Person.prototype.sayHello = function() {
    console.log(`Hello, my name is ${this.name}`);
};

// 创建实例
const alice = new Person('Alice');
alice.sayHello(); // 输出: "Hello, my name is Alice"

原型继承[编辑 | 编辑源代码]

JavaScript使用原型链实现继承。子构造函数的prototype可以指向父构造函数的实例:

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; // 修复constructor引用

Dog.prototype.bark = function() {
    console.log('Woof!');
};

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

Object.create()[编辑 | 编辑源代码]

Object.create()方法可以创建一个新对象,使用现有对象作为新对象的原型:

const personPrototype = {
    greet() {
        console.log(`Hello, I'm ${this.name}`);
    }
};

const carl = Object.create(personPrototype);
carl.name = 'Carl';
carl.greet(); // 输出: "Hello, I'm Carl"

ES6类与原型[编辑 | 编辑源代码]

ES6的class语法是原型的语法糖:

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

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

原型相关方法[编辑 | 编辑源代码]

Object.getPrototypeOf()[编辑 | 编辑源代码]

获取对象的原型:

const obj = {};
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true

Object.setPrototypeOf()[编辑 | 编辑源代码]

设置对象的原型(不推荐在生产环境中使用,影响性能):

const obj = {};
const prototypeObj = { foo: 'bar' };
Object.setPrototypeOf(obj, prototypeObj);
console.log(obj.foo); // "bar"

instanceof 操作符[编辑 | 编辑源代码]

检查构造函数的prototype是否出现在对象的原型链中:

function Foo() {}
const f = new Foo();
console.log(f instanceof Foo); // true
console.log(f instanceof Object); // true

实际应用案例[编辑 | 编辑源代码]

1. 方法共享:通过原型共享方法,节省内存 2. polyfill实现:为旧浏览器添加新功能 3. 库/框架扩展:扩展原生对象功能

方法共享示例[编辑 | 编辑源代码]

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

// 所有实例共享同一个方法
Car.prototype.drive = function() {
    console.log(`${this.model} is driving`);
};

const car1 = new Car('Toyota');
const car2 = new Car('Honda');

car1.drive(); // "Toyota is driving"
car2.drive(); // "Honda is driving"

// 方法只存储一次
console.log(car1.drive === car2.drive); // true

性能考虑[编辑 | 编辑源代码]

1. 原型链越长,属性查找时间越长 2. 避免在运行时修改原型(特别是Object.prototype) 3. 对于频繁访问的属性,可考虑直接存储在对象上

常见误区[编辑 | 编辑源代码]

1. 混淆__proto__prototype

  - prototype是函数对象的属性
  - __proto__是实例对象的属性(指向其原型)
  

2. 认为原型继承是类的拷贝(实际上是引用/委托)

3. 忘记设置constructor引用:

   function Parent() {}
   function Child() {}
   
   // 错误方式:会丢失constructor
   Child.prototype = Parent.prototype;
   
   // 正确方式
   Child.prototype = Object.create(Parent.prototype);
   Child.prototype.constructor = Child;

数学表示[编辑 | 编辑源代码]

原型链可以表示为: objobj.[[Prototype]]obj.[[Prototype]].[[Prototype]]null

总结[编辑 | 编辑源代码]

JavaScript原型是语言的核心特性,理解原型链对于掌握JavaScript面向对象编程至关重要。通过原型: - 实现对象间的继承 - 共享方法和属性 - 构建灵活的代码结构

现代JavaScript虽然提供了class语法,但其底层仍然是基于原型的实现机制。