圖解javascript核心知識點—原型鏈

要理解原型鏈,繞不開constructor、prototype、__proto__這幾個核心的知識點,它們的關係如下:

圖解javascript核心知識點—原型鏈

上面的圖是一個最簡單的原型鏈,先有一個直觀的認識。下面將圍繞上面3個點一步步對原型鏈抽絲剝繭,最後在來總結究竟什麼是原型鏈,自然就清晰了。

2. 課前預習

再正式進入主題之前,先了解幾個知識點

2.1 函數對象與普通對象

JavaScript 中,萬物皆對象,但對象也是有區別的。分為普通對象和函數對象, Object 、Function 是 JS 自帶的函數對象

  • 函數對象
<code>//f1,f2,歸根結底都是通過 new Function()的方式進行創建的
function f1(){};//console.log(typeof f1)==function
var f2 = function(){};//同上
var f3 = new Function('str','console.log(str)');//同上/<code>
  • 普通對象
<code>var o1 = {}; // console.log(typeof o1) == object 
var o2 = new Object();//同上
var o3 = new f1();//同上/<code>

簡單的說,凡是使用 function 關鍵字或 Function 構造函數創建的對象都是函數對象,其他的都是普通對象

圖解javascript核心知識點—原型鏈

2.2 幾個專業術語

  • 顯式原型:prototype
  • 隱式原型:__ proto __
  • 原型對象:每個函數對象都有一個prototype 屬性,這個屬性指向函數的原型對象

3. prototype和contructor

  • prototype指向函數的 原型對象 ,只有函數或者說函數對象才擁有該屬性。
  • contructor指向原型對象的構造函數。

可能很多人對什麼是原型對象比較迷惑,個人是按照下面的模版記得:

原型對象 = 構造函數名.prototype

<code>//Person.prototype就是原型對象
function Person() {}
Person.prototype = {
name: 'Zaxlct',
sayName: function() {
}
}/<code>

3.1 prototype和contructor關係

主要從三個緯度來分析:自定義的構造函數(Person)、Object函數、Function函數

圖解javascript核心知識點—原型鏈

<code>// 可以思考一下打印結果
function Person() {}
console.log(Person.prototype)//[object Object]
console.log(Person.prototype.constructor)//function Person() {}/<code>

3.2 prototype創建時機

在定義函數時自動添加的, 默認值是一個空Object對象

<code>//定義構造函數
function Fn() { // 內部語句: this.prototype = {}
}/<code>

3.3 prototype作用

  • 通過給 Person.prototype 設置屬性和方法之後,Person 的實例都會繼承相應的屬性和方法,所有的實列例共享一份,不會開闢新的空間。
  • prototype用來實現基於原型的繼承與屬性的共享

4. proto

JS 在創建對象(不論是普通對象還是函數對象)的時候,都有一個叫做__proto__ 的內置屬性,用於指向創建它的構造函數的原型對象。

由於js中是沒有類的概念,而為了實現繼承,通過 __ proto __ 將對象和原型聯繫起來組成原型鏈,就可以讓對象訪問到不屬於自己的屬性。

4.1 函數和對象的關係

所有函數都是Function的實例(包含Function),所有構造器都繼承了Function.prototype的屬性及方法。

圖解javascript核心知識點—原型鏈

4.2 原型對象間的關係

所有的原型對象__proto__最終指向了Object.prototype(除了Object.prototype的__proto__之外)。

js原型鏈最終指向的是Object原型對象

圖解javascript核心知識點—原型鏈

4.3 proto創建時機

對象的__proto__屬性: 創建對象時自動添加的, 默認值為構造函數的prototype屬性值

<code>function Fn() {  }  

// 內部語句: this.__proto__ = Fn.prototype
var fn = new Fn() /<code>

4.4 總結

  • 當你new一個構造函數的時候,創建一個函數實例,那麼 『 函數實例.__ proto __ === 該構造函數.prototype 』
  • 所有的函數都是由Function構造出來的,那麼 『被構造出來的其他函數.__ proto __ === Function.prototype 』
  • 所有的構造函數的原型對象都是由Object構造出來的,那麼 『所有的構造函數.prototype.__ proto __ === Object.prototype 』

5. 完整原型鏈結構圖

圖解javascript核心知識點—原型鏈

  • 所有的實例對象都有__proto__屬性, 它指向的就是原型對象
  • 這樣通過__proto__屬性就形成了一個鏈的結構---->原型鏈
  • 當查找對象內部的屬性方法時, js引擎自動沿著這個原型鏈查找
  • 當給對象屬性賦值時不會使用原型鏈, 而只是在當前對象中進行操作

6. 原型鏈的查找和內存表現

<code>function Person() {
this.test1 = function () {
console.log('test1()')
}
}
console.log(Person.prototype)
Person.prototype.test2 = function () {
console.log('test2()')
}
Object.prototype.test3 = function () {
console.log('test3()')
}
const personObj = new Person()
personObj.test1()//test1
personObj.test2()//test2
personObj.test3()//test3
/<code>

對應到內存中的原型鏈如下圖:

圖解javascript核心知識點—原型鏈

7.總結

  • 顯式原型與隱式原型的關係函數的prototype: 定義函數時被自動賦值, 值默認為{}, 即用為原型對象實例對象的__proto__: 在創建實例對象時被自動添加, 並賦值為構造函數的prototype值原型對象即為當前實例對象的父對象
  • 原型鏈的作用避免相同函數重複聲明,減少空間佔用,節省內存實現繼承構造函數的所有實例都可以訪問構造函數原型中的方法和屬性


分享到:


相關文章: