原型和原型鏈的抽筋拔骨

dongfangyiyu發表於2019-03-22

一丶前言

最近的學習中總是時不時的遇到原型相關問題,不勝困擾,於是決定學學哪吒鬧海,對“東海三太子”進行抽筋拔骨。希望你在看完本文之後感受是這樣的:原型和原型鏈的抽筋拔骨


二丶我有故事,君有酒否?

世人皆愛聽故事,在聽各種之乎者也之前不如容小生給大家講個故事。在很久很久以前,js是孤獨的,他沒爹沒媽,沒妻沒子。js轉念一想,總不能叫孤兒吧,於是美其名曰:“null先生”。有一天,null先生撿到了一隻雞和一枚雞蛋,很是高興,給雞蛋取名為Object由於他先看到雞後看到蛋,於是他認為雞是屬於他的,這隻雞呢又是由雞蛋孵化的,為了方便理清這層關係,他使用prototype__proto__來進行區分,並規定:

Object.prototype = 雞;
雞.__proto__ = null;複製程式碼

以圖示之:

原型和原型鏈的抽筋拔骨

null先生並不安分,他認為雞給他帶來了很多樂趣,他得回報啊,於是他動手為雞造了一個雞窩,並規定,這個雞窩是給雞的,於是:

雞窩.__proto__ = 雞;複製程式碼

以圖示之:

原型和原型鏈的抽筋拔骨

自從有了這個雞窩,null先生從此和雞過上了沒羞沒臊的生活。。。。。。額,不是,是有了這個雞窩,雞居然生小雞了,但凡這隻雞走進雞窩,沒多久就能從裡面產出小雞來,amazing!null先生想這肯定是這個雞窩的功勞,興奮之餘,他發現雖然都是雞,但顏色,大小等都各有差異,大家都是雞,你裝什麼高貴。null先生雖然這麼想,但為了區分還是根據它們的特性進行命名:StringNumberBooleanArray。。。。。。這時null先生再望向那枚起始的雞蛋,一拍大腿,雞生蛋,蛋生雞,這枚雞蛋(Object)也可以歸屬到雞窩才是!

Object.__proto__ = 雞窩;複製程式碼

null先生以為日子總該平靜了吧,但雞就是雞,一天不搞事渾身難受,有一種渾身彩色的小雞我們簡稱為彩雞,它幹了什麼呢?它居然可以自己造窩,不僅如此,它造出來的雞窩和null先生造的雞窩功能是一樣的,也能產小雞,null先生不禁歎服:好你個彩雞,居然有如此之本領,這般放蕩,要剋制自己的本性才好。於是給這種彩雞取名為“放克性”,彩雞不服,兄弟姐妹們都是英文名憑啥我是中文名,便自我改名Function。有了這層關係,我們大膽得出結論:

String.__proto__ = 雞窩;
Number.__proto__ = 雞窩;
...
Function.__proto__ = 雞窩;
Function.prototype = 雞窩;複製程式碼

以圖示之:

原型和原型鏈的抽筋拔骨

啊~null先生大嘆一聲,這日子什麼時候是個頭啊!其實,這才剛剛開始。。。。。。

好了,故事講完了,根據以上資訊我們得出以下結論:

Object.__proto__ === Function.prototype;
Function.prototype.__proto__ === Object.prototype;
Object.prototype.__proto__ === null;複製程式碼

三丶給物件劃分三六九等

我們常說“萬物皆物件”是因為javascript任何值或變數都是物件,但是物件也可以劃分為三六九等。

公爵——Object

Javascript中所有的物件都是Object的例項,並繼承Object.prototype的屬性和方法,所有的物件都是間接或直接的通過Object衍生的。這個大哥Object當之無愧。

侯爵——Function

當我們定義一個物件時,可以用var obj = new Object(),我們都知道new是用來例項化一個物件的方法,但是我們都知道javascript中不存在真正的類的概念,所以只能用函式來模擬,那麼既然可以有上面的做法也就印證了Object也是特殊的函式。  我們回到剛才的故事,Object是蛋,Function是雞,那你覺得是雞生蛋還是蛋生雞呢?成年人沒空思考,小孩又想不出答案。。。。。。

伯爵——String,Number,Boolean,Array...

子爵——其他

四丶原型和原型鏈

講了這麼多,那麼到底何為原型,何為原型鏈呢???

4.1 原型

每個函式都有prototype(原型)屬性,這個屬性是一個指標,指向一個物件,這個物件的用途是包含特定型別的所有例項共享的屬性和方法,我們稱這種物件為原型物件,即這個原型物件是用來給例項共享屬性和方法的。也就是說Function.prototype = 原型物件,現在再想想那群雞和雞窩是不是恍然大悟,雞窩原來就是原型物件!

既然函式這麼特殊,那麼我們在此將物件分為函式物件普通物件,函式物件是通過使用function定義的物件,而普通物件是通過使用new操作符生成的物件。函式物件有一個prototype屬性,普通物件就沒有這個prototype屬性,存在__proto__。我們來看幾個例子:

var obj = new Object();
console.log(obj.__proto__);//Object原型物件
console.log(obj.prototype);//undefined
console.log(Object.prototype);//Object原型物件
console.log(obj.__proto__ === Object.prototype)//true


var Fn = function(){};
var fn = new Fn();
console.log(Fn.prototype);//Object {constructor: function}
console.log(fn.__proto__);//Object {constructor: function}
console.log(fn.__proto__ === Fn.prototype);//true複製程式碼

實際上每個例項內部都有一個指向原型物件的指標。因此:若var a = new A();則a.__proto__=A.prototype

4.2 原型鏈

談到原型鏈就不得不談到__proto__,上面我們有談到,每個物件都會在其內部初始化一個屬性,就是__proto__當我們訪問一個物件的屬性時,如果這個物件內部不存在這個屬性,那麼它就會去__proto__裡找這個屬性,這個__proto__又會有自己的__proto__,於是就這樣 一直找下去,也就是我們平時所說的原型鏈的概念。舉一個建構函式中經常遇到的例項:

var Fn = function(){};
Fn.prototype.sayLove = function(){
    console.log("I love XX");
}
var fn = new Fn();
fn.sayLove();//I love XX;複製程式碼

當我們呼叫fn.sayLove()時會輸出"I love XX",那麼fn是如何找到這個方法的呢?首先fn作為例項物件,本身並不存在sayLove()方法,從而它就會去找fn.__proto__,因為fn是Fn的例項,因此fn.__proto__ = Fn.prototype,而我們上邊定義了Fn.prototype.sayLove = function(){},因此fn.sayLove()自然也就輸出了"I love XX"。

初來貴寶地,有錢的捧個贊場,沒錢的點個關注再走。

五丶參考資料

深入理解JavaScript原型鏈
Object.prototype原型和原型鏈
JS原型和原型鏈是什麼?
JavaScript 世界萬物誕生記


相關文章