prototype
屬性,是一個物件,用來存放共享的方法或屬性。 __proto__
連接到其原型,形成屬性和方法的查找路徑。function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function() {
console.log(`Hi, I'm ${this.name}`);
};
const p1 = new Person("Alice", 25);
const p2 = new Person("Bob", 30);
p1.sayHi(); // "Hi, I'm Alice"
p2.sayHi(); // "Hi, I'm Bob"
console.log(p1.sayHi === p2.sayHi); // true(共用同一個方法)
記憶體優勢
如果每個實例都帶一個 sayHi
函數,會浪費記憶體,因為這些函數功能相同,只是 this
不同,而將方法定義在 Person.prototype
上,所有實例共享,就能節省記憶體。
p1
和 p2
各有一份 sayHi
,佔用不同記憶體位置(p1.sayHi !== p2.sayHi
)。sayHi
只有一份,實例仍獨立,但方法共享。用 new
創建實例時,實例的 __proto__
內部屬性會自動設為建構函數的 prototype
,形成查找鏈。
const p = new Person("Alice");
console.log(p.__proto__ === Person.prototype); // true
console.log(p.hasOwnProperty("sayHi")); // false(不在 p 自身)
console.log(p instanceof Person); // true
p.__proto__
-> Person.prototype
Person.prototype.__proto__
-> Object.prototype
Object.prototype.__proto__
-> null
查找過程:
訪問屬性或方法時,從實例自身開始,沿原型鏈向上查找。
1.p
是什麼?
p 是用 new Person("Alice") 創建的實例。
2.p.__proto__
是什麼?
__proto__ 是每個物件都有的內部屬性,指向它的「原型」。
當你用 new Person 創建 p 時,JavaScript 自動設定:
p.__proto__ = Person.prototype
3.Person.prototype
是什麼?
它是 Person 建構函數的一個屬性,預設是個物件。
4.Person.prototype.__proto__
是什麼?
Person.prototype 本身也是一個物件,所以它也有自己的 __proto__。
因為它是普通物件,它的 __proto__ 指向 Object.prototype
5.Object.prototype
是什麼?
它是 JavaScript 中所有物件的「老祖宗」,包含一些基本方法(如 toString、hasOwnProperty)。
預設由 Object 建構函數提供。
6.Object.prototype.__proto__
是什麼?
Object.prototype 是鏈的頂端,它的 __proto__ 是 null。
表示「沒有更上面的原型了」,查找就停在這裡。
prototype
既可以定義方法,也可以定義屬性,實例都能透過原型鏈繼承。
Person.prototype.species = "Human"; // 原型上的屬性
console.log(p1.species); // "Human"(從原型繼承)
console.log(p2.species); // "Human"
console.log(p1.species === p2.species); // true(共享)
為什麼較少用來放屬性?
原型上的屬性是共享的,若修改可能影響所有實例,不適合個別化的資料。
若屬性需個別值(如 name),應放實例自身。
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
function Dog(name) {
this.name = name;
}
Dog.prototype = new Animal(); // 繼承:把 Dog 的原型設為 Animal 的實例
Dog.prototype.bark = function() {
console.log(`${this.name} says woof`);
};
const dog = new Dog("Max");
dog.eat(); // "Max is eating"(從 Animal 繼承)
dog.bark(); // "Max says woof"(Dog 自己的)
// 驗證
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
步驟解析:
原型鏈:
dog.__proto__
-> Dog.prototype
(new Animal()
的實例)Dog.prototype.__proto__
-> Animal.prototype
Animal.prototype.__proto__
-> Object.prototype
1.屬性遮蔽:實例上的同名屬性會覆蓋原型上的屬性。
dog.eat = function() {
console.log("Dog-specific eating");
};
dog.eat(); // "Dog-specific eating"(遮蔽原型)
2.避免循環引用:確保原型鏈不會無限循環。例如: 若誤設循環引用(如 A.prototype.__proto__ = A),查找會無限循環。