1. 程式人生 > >JavaScript Prototype原型原型鏈詳解

JavaScript Prototype原型原型鏈詳解

Prototype屬於比較底層的知識,學習之前我要先說一句話.

建構函式.prototype 是個物件(有唯一例外,Function.prototype),__proto__指向建立他的建構函式的原型物件。
如果沒理解這句話,就說明沒有理解原型。
開門見圖,很關鍵!!
這裡寫圖片描述

下面很多知識點都是重複的,就當鞏固了吧。

  • 新建例項

萬物皆物件,每個物件都有一個屬性叫 __proto__,但是prototype屬性只有通過Function方式新建的物件才有,這種物件稱為函式物件,他們會有一個prototype屬性。

function foo(){}  或者
let foo = new
Function();

其他建立方式,比如

function foo(){}   //前提
let a = new foo()  或者
let a = new Object(); 或者
let a = {}

都沒有prototype屬性。那這兩個屬性是什麼意思呢?
答:prototype指向函式的原型物件,__proto__指向建立它的建構函式的原型物件。看程式碼。

function foo(){}
foo.prototype 為 Object(即原型物件)[注:這裡指的是,是個物件而不是foo.prototype === Object]

let foo1 = new foo();  //建立foo1的建構函式是foo
foo1.__proto__ => foo.prototype;
  • 建構函式
function foo(){}   foo就是一個建構函式
let foo1 = new foo();
let a = {}//等同於 new Object()

foo1.constructor===foo  //true
a.constructor===Object   //true   a為執行方法得到的結果,Object為方法本身

上述程式碼可知:例項的建構函式指向建構函式(有點像屁話)。

foo.prototype.constructor === foo

foo1.constructor === foo

foo1.__proto__===foo.prototype
foo1.prototype undefined 前面說了,只有通過Function方式新建的物件才有`prototype`

稍微總結一下:建構函式的原型物件是建構函式的一個例項,是一個物件。

問:foo.prototype.__proto__ 是什麼?
答:foo.prototype是一個foo的例項物件,所以他的建構函式是Object,
所以答案是Object.prototype
這裡昇華一下,讓我們看看構造的起源在哪裡?
JS中有兩個根構造器,ObjectFunction.他們的__proto__都是Function.prototype,自定義一個構造器
function foo(){}
foo.__proto__也是Function.prototype。因為Function是foo的建構函式。
最後
Function.prototype.__proto__ === Object.prototype
這裡說一句,Function.prototype是一個物件,建構函式是Object,所以等式成立。
- 作用:
原型物件主要是用來實現繼承的。

function cat(food){this.food = food}
cat.prototype.say = function(){
    alert("I love eat"+this.food);
}
var test = new cat("fish");
test.say();  //I love eat fish
cat.say();   //undefined

test從原型鏈繼承了say方法。而且從上面cat.say();//undefined 來看,原型是服務於繼承的屬性,本身是訪問不到的。__proto__的源頭是Object.prototype,內建函式都定義在其中。

  • 再議prototype
    prototype儲存著所有的例項方法,我們知道JS內建了方法,toString(),valueOf等。
    首先,剛才說過,物件的起源是Function.prototype下面看程式碼。
foo.__proto__ === Function.prototype
然後列印Function.prototype發現是一個空函式{這是唯一一個.prototype為函式的存在}
然後你再列印一個
Function.prototype.__proto__ 你會發現這個裡面存在著需要的所有方法,這些都是繼承下來的!!

所有的追根溯源都是通過__proto來進行的,當讀取變數或函式時,順序如下

foo1.name  
foo1.__proto__.name 即 foo.prototype.name
foo1.__proto__.__proto__.name =>Object.prototype.name
foo1.__proto__.__proto__.__proto__=>null 讀到null為頂端

最後說一句話,當你探索的時候,過程是痛苦的,但是等到你探索成功之後,你會發現所有的困惑都會慢慢趨向清晰。