JavaScript 是一種弱類型、基於原型的編程語言。原型是 JavaScript 的一項非常重要的特性,也是面試中經常考察的知識點。JS 原型鏈是基於原型的面向對象編程的基石,掌握這一知識點對於我們理解 JavaScript 的對象模型非常重要。本文將帶你從多個方面深入剖析JavaScript原型鏈面試題。
一、JS原型鏈基本概念
在 JavaScript 中,每個函數都有一個 prototype 屬性,也就是原型對象。原型對象是一個普通的對象,包含屬性和方法。每個實例對象都通過 __proto__ 屬性引用其構造函數的原型對象 prototype。
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { console.log(`Hello, I'm ${this.name}, I'm ${this.age} years old.`); } const person = new Person('Tom', 18);
在以上代碼中,Person 函數有一個 prototype 屬性,它是一個普通的對象,並且包含一個方法 sayHello。person 是使用 Person 函數構造出來的實例對象, 它通過 __proto__ 屬性引用了 Person 的原型對象 Person.prototype,因此它可以使用原型對象中的方法 sayHello。
二、JS原型鏈的構建方式
原型鏈是由每個對象的 __proto__ 屬性構成的。我們可以通過以下方式構建原型鏈:
1. 新建一個構造器函數
2. 擴展構造器的原型對象
3. 使用 new 操作符創建子對象
4. 構造器原型對象的 __proto__ 屬性指向構造器父級的原型對象
5. 子對象的 __proto__ 屬性指向了構造器的原型對象,也就是形成了原型鏈
function Animal(name) { this.name = name; } Animal.prototype.sayName = function () { console.log(this.name); } function Dog(name, age) { this.age = age; Animal.call(this, name); } Dog.prototype = Object.create(Animal.prototype); // Dog 繼承 Animal Dog.prototype.constructor = Dog; // constructor 指向 Dog Dog.prototype.sayAge = function () { console.log(this.age); } const dog = new Dog('Lily', 1);
在以上代碼中,Dog 繼承了 Animal 的方法以及屬性。通過 Dog.prototype = Object.create(Animal.prototype),Dog 的原型指向了 Animal 的原型,因此它可以使用 Animal 的原型中的方法 sayName,在 Dog 的原型中添加 sayAge 方法。
三、JS原型鏈的查找順序
當我們在一個實例對象上調用一個屬性或者方法時,JavaScript 引擎會按照如下的順序查找屬性或方法:
1. 首先查找實例對象本身是否有該屬性或方法
2. 如果沒有,則查找實例對象的原型對象是否有該屬性或方法
3. 如果還沒有,則查找實例對象原型對象的原型對象是否有該屬性或方法
4. 重複上述步驟,直到查找到 Object 的原型對象,即可結束查找
function Animal(name) { this.name = name; } Animal.prototype.sayName = function () { console.log(this.name); } function Dog(name, age) { this.age = age; Animal.call(this, name); } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.sayAge = function () { console.log(this.age); } const dog = new Dog('Lily', 1); console.log(dog.__proto__ === Dog.prototype); // true console.log(Dog.prototype.__proto__ === Animal.prototype); // true console.log(Animal.prototype.__proto__ === Object.prototype); // true
在以上代碼中,dog 是 Dog 的實例對象。當我們在 dog 上調用 sayAge 方法時,首先查找 dog 本身是否有該方法,發現沒有。然後查找 Dog.prototype 是否有該方法,發現有。因此調用了該方法並輸出了 dog 的年齡。在這個查找的過程中,JS 引擎按照 __proto__ 屬性指向的原型對象繼續查找,直到最後找到 Object 的原型對象 Object.prototype 為止。
四、JS原型鏈的繼承方式
在 JavaScript 中,有很多實現繼承的方法。下面分別介紹一下常用的幾種繼承方式:
1. 構造函數繼承
構造函數繼承是一種常用的繼承方式。其基本思想是在子類的構造函數中調用父類的構造函數。
function Parent(age) { this.age = age; } function Child(age) { Parent.call(this, age); } const child = new Child(18); console.log(child.age); // 18
在以上代碼中,Child 函數的構造函數中調用了 Parent 的構造函數並傳入參數。Child 實例可以訪問到 Parent 實例中的屬性 age。
2. 原型鏈繼承
原型鏈繼承的基本思想是通過將子類的原型對象指向父類的實例對象來實現繼承。
function Parent(age) { this.age = age; } Parent.prototype.sayAge = function () { console.log(this.age); }; function Child(age) {} Child.prototype = new Parent(18); const child = new Child(); console.log(child.age); // 18 child.sayAge(); // 18
以上代碼中,Child 的原型指向了 Parent 的實例,並且 Child 的實例可以訪問到 Parent 中的方法和屬性。
3. 組合繼承
組合繼承是上述兩種繼承方式的結合。其基本思想是通過構造函數繼承實現屬性的繼承,通過原型鏈繼承實現方法的繼承。
function Parent(age) { this.age = age; } Parent.prototype.sayAge = function () { console.log(this.age); }; function Child(age) { Parent.call(this, age); } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; const child = new Child(18); console.log(child.age); // 18 child.sayAge(); // 18
以上代碼中,Child 函數的構造函數中調用了 Parent 的構造函數並傳入參數,實現了屬性的繼承。Child 的原型對象指向了 Parent 的原型對象,並且將 constructor 指向了 Child 函數,實現了方法的繼承。
結語
JavaScript 原型鏈是一種非常重要的特性。理解原型鏈能夠幫助我們更好地理解 JavaScript 的對象模型和繼承機制。掌握 JavaScript 原型鏈的實現方式以及查找順序可以幫助我們更好地回答面試題。在實際項目中,可以根據練習的需求選擇不同的繼承方式以實現需求。
原創文章,作者:XGIKP,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/363902.html