一、什麼是function.prototype
在JavaScript中,每個函數都有一個屬性叫做prototype,這個屬性本質上是一個對象,在定義函數時自動創建。函數的prototype屬性指向的是一個空對象(即Object對象的實例),這個空對象通常被稱為「原型對象」。
當我們定義一個函數後,JavaScript會按照特定的規則自動為這個函數創建一個prototype對象,並將其賦值給該函數的prototype屬性。也就是說,所有該函數產生的實例,都可以繼承該prototype對象的屬性和方法。
舉個例子:
function Person() {}
Person.prototype.name = "Jack";
Person.prototype.sayName = function() { console.log(this.name); };
var person1 = new Person();
person1.sayName(); // Jack
上述代碼定義了一個空函數Person,然後為Person的prototype屬性添加了name和sayName兩個屬性。接著通過new關鍵字創建了一個person1的實例。因為person1沒有name屬性,所以訪問person1.sayName()時,this.name取的就是person1的原型對象Person.prototype的name屬性。
二、原型鏈
在JavaScript中,我們可以通過對象的__proto__屬性訪問其構造函數的原型對象,在原型對象中可以繼續訪問到最頂層的Object原型對象。這樣的一系列原型對象就構成了「原型鏈」,如下圖所示:
在實際應用中,如果訪問一個對象的某個屬性或方法,首先會在該對象的實例中查找,如果沒有找到,則會沿著原型鏈往上查找,直到最頂層的Object原型對象為止,如果還沒有找到,則返回undefined。
三、構造函數、實例和原型
在JavaScript中,通過構造函數來創建對象實例是很常見的做法,但是如果我們直接在原型對象上添加屬性或方法,有可能會出現共享的問題。例如:
function Person() {}
Person.prototype.friends = ["Alice", "Bob"];
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Cindy");
console.log(person1.friends); // ["Alice", "Bob", "Cindy"]
console.log(person2.friends); // ["Alice", "Bob", "Cindy"]
上述代碼定義了一個空函數Person,然後為Person的prototype屬性添加了friends屬性。接著通過new關鍵字創建了person1和person2兩個實例。然而,person1和person2共享了Person.prototype中的friends這個數組,導致person1操作friends數組後,person2也受到了影響。
為了避免這種問題,我們可以在構造函數中定義實例屬性,實例屬性在創建對象時會被賦值為新的數組,從而避免了實例之間的共享問題:
function Person() {
this.friends = ["Alice", "Bob"];
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Cindy");
console.log(person1.friends); // ["Alice", "Bob", "Cindy"]
console.log(person2.friends); // ["Alice", "Bob"]
除了在構造函數中定義屬性外,我們還可以在原型對象上定義「引用類型」(如數組、對象等)的屬性,從而避免了實例之間的共享問題。
四、通過原型繼承
在JavaScript中,由於函數和對象的本質相同,因此我們可以讓一個函數「繼承」另一個函數的prototype對象,從而在原型鏈上實現「繼承」的效果。例如:
function Animal() {}
Animal.prototype.species = "動物";
function Cat(name) {
this.name = name;
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("小黑");
console.log(cat1.species); // 動物
上述代碼中,定義了一個空函數Animal,並在其prototype對象上定義了一個species屬性。接著,定義了一個構造函數Cat,並將其prototype屬性指向了一個Animal的實例。即使Cat本身沒有定義species屬性,但是在它的原型鏈上找得到,所以可以輸出「動物」。
五、使用Object.create()創建對象
除了使用構造函數來創建對象外,還可以使用Object.create()方法來創建新的對象,並將其原型對象指定為另一個對象。例如:
var Animal = {
species: "動物"
};
var Cat = Object.create(Animal);
Cat.name = "小黑";
console.log(Cat.species); // 動物
上述代碼中,首先定義了一個Animal對象,然後使用Object.create()方法創建了一個新的Cat對象,並將其原型對象指定為Animal。因此,Cat對象可以訪問到Animal對象中的屬性。
六、總結
通過本文的介紹,我們能夠更加深入地了解function.prototype的作用、原型鏈、構造函數、實例和原型、通過原型實現繼承以及使用Object.create()創建對象等方面的內容。在實際應用中,對於這些知識的掌握可以幫助我們更好地利用JavaScript的特性,進行高效的開發。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/270725.html