原型和原型链

38 阅读1分钟

1. 基本概念

JavaScript 中的每个对象都有一个内部属性 [[Prototype]](可以通过 __proto__ 访问),指向它的原型对象。这样就形成了一个原型链,直到 null

2. 原型关系图

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

// 原型方法
Person.prototype.sayHello = function() {
    console.log(`Hello, I'm ${this.name}`);
}

// 创建实例
const person = new Person('John');

关系图:

person.__proto__ === Person.prototype
Person.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null

Person.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype

3. 详细代码示例

// 1. 构造函数方式
function Animal(name) {
    this.name = name;
}

Animal.prototype.makeSound = function() {
    console.log('Some sound');
}

// 2. 类方式(ES6+)
class Dog extends Animal {
    constructor(name) {
        super(name);
    }
    
    bark() {
        console.log('Woof!');
    }
}

// 3. 创建实例
const dog = new Dog('Max');

// 4. 原型链检查
console.log(dog.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true

// 5. instanceof 检查
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true

4. 原型继承的几种方式

// 1. 原型链继承
function Parent() {
    this.name = 'parent';
}

function Child() {
    this.type = 'child';
}

Child.prototype = new Parent();

// 2. 构造函数继承
function Child2() {
    Parent.call(this);
    this.type = 'child2';
}

// 3. 组合继承
function Child3() {
    Parent.call(this);
    this.type = 'child3';
}
Child3.prototype = new Parent();
Child3.prototype.constructor = Child3;

// 4. 寄生组合继承
function Child4() {
    Parent.call(this);
    this.type = 'child4';
}
Child4.prototype = Object.create(Parent.prototype);
Child4.prototype.constructor = Child4;

// 5. ES6 class 继承
class Parent5 {
    constructor() {
        this.name = 'parent';
    }
}

class Child5 extends Parent5 {
    constructor() {
        super();
        this.type = 'child5';
    }
}

5. 实用方法和属性

// 1. 检查属性是否存在于原型链中
function hasPrototypeProperty(obj, name) {
    return name in obj && !obj.hasOwnProperty(name);
}

// 2. 获取原型
const proto = Object.getPrototypeOf(obj);

// 3. 设置原型
Object.setPrototypeOf(obj, prototype);

// 4. 创建没有原型的对象
const noProto = Object.create(null);

// 5. 检查原型链
function isInPrototypeChain(parent, child) {
    return child instanceof parent;
}

6. 实际应用示例

// 1. 扩展内置对象
Array.prototype.first = function() {
    return this[0];
}

const arr = [1, 2, 3];
console.log(arr.first()); // 1

// 2. 实现继承
class Component {
    constructor(props) {
        this.props = props;
    }
    
    render() {
        throw new Error('Must implement render');
    }
}

class Button extends Component {
    render() {
        return `<button>${this.props.text}</button>`;
    }
}

// 3. 混入模式
const shareMethods = {
    share() {
        console.log('Sharing...');
    }
};

Object.assign(Component.prototype, shareMethods);

要点:

  1. 原型的基本概念:
  • 每个函数都有 prototype 属性

  • 每个对象都有 proto 属性

  • 原型链的终点是 null

  1. 继承的实现方式:
  • 原型链继承

  • 构造函数继承

  • 组合继承

  • 寄生组合继承

  • ES6 class 继承

  1. 原型链查找机制:
  • 先查找对象自身属性

  • 没有则查找原型

  • 沿着原型链向上查找

  • 直到 null

  1. 性能考虑:
  • 原型方法共享,节省内存

  • 原型链过长会影响查找性能

  • 避免在原型上存储引用类型数据

  1. 实际应用场景:
  • 继承实现

  • 方法共享

  • 扩展内置对象

  • 混入模式