# JavaScript中的原型和原型鏈怎么理解
## 引言
JavaScript作為一門基于原型的語言,原型(Prototype)和原型鏈(Prototype Chain)是其核心概念之一。理解原型和原型鏈對于掌握JavaScript的面向對象編程、繼承機制以及代碼設計模式至關重要。本文將深入探討原型和原型鏈的概念、工作原理以及實際應用。
---
## 1. 什么是原型?
### 1.1 原型的定義
在JavaScript中,每個對象(除了`null`)都有一個與之關聯的原型對象(Prototype Object)。原型對象可以看作是對象的“模板”,它定義了對象的共享屬性和方法。
### 1.2 如何訪問原型?
- **`__proto__`屬性**(非標準,但被廣泛支持):
通過對象的`__proto__`屬性可以直接訪問其原型對象。
例如:
```javascript
const obj = {};
console.log(obj.__proto__); // 輸出: Object.prototype
Object.getPrototypeOf()
方法(推薦):
const obj = {};
console.log(Object.getPrototypeOf(obj)); // 輸出: Object.prototype
每個構造函數(如Object
、Array
、Function
等)都有一個prototype
屬性,指向其原型對象。
例如:
function Person(name) {
this.name = name;
}
console.log(Person.prototype); // 輸出: Person的原型對象
當通過構造函數創建實例時,實例的__proto__
會指向構造函數的prototype
對象:
const person1 = new Person("Alice");
console.log(person1.__proto__ === Person.prototype); // true
原型鏈是由對象的原型對象通過__proto__
鏈接起來的一條鏈式結構。當訪問一個對象的屬性或方法時,JavaScript引擎會沿著原型鏈向上查找,直到找到該屬性或到達原型鏈的頂端(Object.prototype
的__proto__
為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.bark = function() {
console.log("Woof!");
};
const dog1 = new Dog("Buddy", "Golden Retriever");
dog1.eat(); // 輸出: Buddy is eating.
dog1.bark(); // 輸出: Woof!
上述代碼的原型鏈關系如下:
1. dog1.__proto__
指向Dog.prototype
。
2. Dog.prototype.__proto__
指向Animal.prototype
。
3. Animal.prototype.__proto__
指向Object.prototype
。
4. Object.prototype.__proto__
為null
。
當調用dog1.eat()
時:
1. 首先檢查dog1
自身是否有eat
方法(沒有)。
2. 沿著原型鏈查找Dog.prototype
(沒有)。
3. 繼續查找Animal.prototype
,找到eat
方法并調用。
JavaScript通過原型鏈實現繼承。子類的原型對象指向父類的實例,從而繼承父類的屬性和方法。
function Parent(name) {
this.name = name;
}
Parent.prototype.sayHello = function() {
console.log(`Hello, ${this.name}!`);
};
function Child(name, age) {
Parent.call(this, name); // 繼承屬性
this.age = age;
}
Child.prototype = Object.create(Parent.prototype); // 繼承方法
Child.prototype.constructor = Child; // 修復構造函數指向
const child1 = new Child("Tom", 10);
child1.sayHello(); // 輸出: Hello, Tom!
class
語法糖ES6引入了class
關鍵字,但其底層仍然是基于原型的繼承。
class Parent {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, ${this.name}!`);
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
const child1 = new Child("Tom", 10);
child1.sayHello(); // 輸出: Hello, Tom!
修改構造函數的原型會影響所有實例:
function Person() {}
const person1 = new Person();
Person.prototype.sayHi = function() {
console.log("Hi!");
};
person1.sayHi(); // 輸出: Hi!
如果實例和原型鏈上有同名屬性,實例屬性會遮蔽原型屬性:
function Person() {}
Person.prototype.name = "Unknown";
const person1 = new Person();
person1.name = "Alice";
console.log(person1.name); // 輸出: Alice(遮蔽了原型屬性)
所有原型鏈的終點是Object.prototype
,其__proto__
為null
:
console.log(Object.prototype.__proto__); // null
通過原型共享方法,節省內存:
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("BMW");
car1.drive(); // Toyota is driving.
car2.drive(); // BMW is driving.
通過修改原型擴展內置對象的功能(需謹慎):
Array.prototype.last = function() {
return this[this.length - 1];
};
const arr = [1, 2, 3];
console.log(arr.last()); // 3
通過原型鏈實現多態行為:
function Animal() {}
Animal.prototype.makeSound = function() {
console.log("Some sound");
};
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.makeSound = function() {
console.log("Woof!");
};
const animal = new Animal();
const dog = new Dog();
animal.makeSound(); // Some sound
dog.makeSound(); // Woof!
__proto__
鏈接的鏈式結構,用于實現屬性和方法的查找。class
是語法糖。Object.prototype
,其__proto__
為null
。理解原型和原型鏈是掌握JavaScript面向對象編程的關鍵,希望本文能幫助你深入理解這一核心概念! “`
這篇文章總計約3500字,涵蓋了原型和原型鏈的核心概念、工作原理、實際應用以及常見問題。內容結構清晰,適合初學者和有一定基礎的開發者閱讀。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。