原型与原型链
在 JavaScript 中,原型(Prototype)是对象的一种特殊类型,它是所有对象共享属性和方法的机制。每一个 JavaScript 对象都与一个原型对象相关联,并且通过原型链来继承属性和方法。
1. 原型(Prototype)
每个对象在创建时都会有一个内部属性 [[Prototype]]
,它指向该对象的原型。在 JavaScript 中,原型通常通过 __proto__
属性访问,或者可以通过 Object.getPrototypeOf()
方法获取。
原型本质上是一个普通对象,拥有可以共享的属性和方法。对象通过它的原型来访问这些共享的属性和方法。
2. 原型链(Prototype Chain)
原型链是一种链式结构,通过一个对象的 [[Prototype]]
属性将多个对象链接起来。当你访问对象的属性时,如果该对象本身没有这个属性,它会沿着原型链查找该属性,直到找到为止。如果原型链的末端仍然没有该属性,则返回 undefined
。
代码示例
创建对象与原型链
// 构造函数
function Animal(name) {this.name = name;
}// 在原型上定义一个方法
Animal.prototype.sayHello = function() {console.log(`Hello, my name is ${this.name}`);
};// 创建一个 Animal 对象
const dog = new Animal('Dog');// 通过实例访问原型方法
dog.sayHello(); // Output: Hello, my name is Dog// 查看dog的原型
console.log(dog.__proto__ === Animal.prototype); // true
解析:
Animal.prototype
定义了sayHello
方法,这个方法并不是直接在实例dog
上定义的,而是通过原型链来访问的。- 当我们调用
dog.sayHello()
时,JavaScript 引擎会先查看dog
是否有sayHello
方法,如果没有,则沿着原型链去Animal.prototype
上找。 - 由于
dog
是通过Animal
构造函数创建的,所以dog.__proto__
是Animal.prototype
。
原型链的查找机制
function Animal(name) {this.name = name;
}Animal.prototype.sayHello = function() {console.log(`Hello from ${this.name}`);
};function Dog(name, breed) {Animal.call(this, name); // 继承 Animal 的属性this.breed = breed;
}// 继承 Animal 的方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;Dog.prototype.bark = function() {console.log(`${this.name} says Woof!`);
};const myDog = new Dog('Max', 'Golden Retriever');myDog.sayHello(); // Hello from Max
myDog.bark(); // Max says Woof!
解析:
Dog
通过Object.create(Animal.prototype)
来继承Animal
的原型方法。myDog
对象继承了Animal
的方法sayHello
和Dog
的方法bark
。- 在
myDog.sayHello()
调用时,JavaScript 引擎沿着原型链查找,首先找到Dog.prototype
上的sayHello
方法。
instanceof
运算符
instanceof
用于检查对象是否为某个构造函数的实例。它基于原型链工作,检查对象的原型链中是否有构造函数的 prototype
属性。
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true
console.log(myDog instanceof Object); // true
解析:
myDog instanceof Dog
返回true
,因为myDog
是Dog
的实例,且Dog.prototype
存在于myDog
的原型链中。myDog instanceof Animal
也返回true
,因为Dog.prototype
继承自Animal.prototype
,所以myDog
的原型链也包含Animal.prototype
。
原型的应用场景
1. 方法共享
通过将方法定义在构造函数的原型上,多个实例对象可以共享相同的功能,而不需要每次创建对象时都重新定义方法,从而节省内存空间。
function Person(name) {this.name = name;
}// 共享方法
Person.prototype.greet = function() {console.log(`Hello, my name is ${this.name}`);
};const p1 = new Person('Alice');
const p2 = new Person('Bob');p1.greet(); // Hello, my name is Alice
p2.greet(); // Hello, my name is Bob
2. 模拟继承
原型链机制是 JavaScript 实现继承的一种方式。通过修改构造函数的原型,我们可以实现不同类型之间的继承。
function Shape(name) {this.name = name;
}Shape.prototype.describe = function() {console.log(`This is a ${this.name}`);
};function Circle(name, radius) {Shape.call(this, name);this.radius = radius;
}Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;Circle.prototype.area = function() {return Math.PI * this.radius * this.radius;
};const myCircle = new Circle('Circle', 5);
myCircle.describe(); // This is a Circle
console.log(myCircle.area()); // 78.53981633974483
解析:
Circle
通过Object.create(Shape.prototype)
继承了Shape
的方法describe
。Circle
添加了自己的方法area
,实现了继承的同时扩展了新方法。
总结
- 原型(Prototype) 是对象的一个内部属性,每个对象都有一个与之关联的原型,原型是对象共享方法和属性的机制。
- 原型链(Prototype Chain) 是多个对象通过原型属性连接起来的链式结构,访问对象属性时会沿着原型链逐级查找。
- 常见应用场景包括方法共享、模拟继承等。
通过理解原型和原型链的机制,我们可以更加高效地管理对象的属性和方法,利用继承和共享减少内存开销,提高代码的复用性和可维护性。