【設計模式+原型理解】第四章:函式的三種角色+原型鏈終結版
一、函式的三種角色
1)作為普通函式
2)作為物件
3)作為類
ps:可以同時存在,之間沒有任何衝突
二、怎麼理解函式的三種角色
這三種角色可以同時存在,沒有任何衝突,舉個例子
// 這三種角色是沒有衝突的,看下面的例子 function Fn1() { // 這時候,Fn就是一個普通函式,形參賦值,預解釋,程式碼執行 var num = 500 this.x = 100; } Fn.prototype.getX = function () { console.log(this.x); }; Fn.aaa = 1000; // 這時候,Fn就是作為一個物件(有私有屬性,有方法,還有原型) var f = new Fn; // 這時候,Fn就是一個類,可以通過new建立物件 ,this指向的是f!! console.log(f.num); //->undefined這個時候,Fn就是一個類,num是Fn作為普通函式才有用,num跟類完全沒關係 console.log(f.aaa); //->undefined這個時候,Fn就是一個類,aaa是Fn作為物件才有用,aaa跟類完全沒有關係 var res = Fn(); console.log(res); //->undefined這個時候,Fn就是一個普通函式,函式裡沒返回值,this指向的是window!!!!!!!!
上面的例子完美的解釋了,函式三種角色真的沒有任何聯絡。
感覺函式真的是模擬了真實人生,就好比我帶著我爸媽,帶著我閨女,我作為人類、作為父親、作為兒子,沒有任何衝突,該幹嘛幹嘛!!
上面的程式碼,可以列印一下Fn函式裡面到底有什麼東西。
dir(Fn)
輸出資訊如下:
可以看到,Fn作為物件,有自己的屬性值(arguments、caller、length、name、prototype、__proto__)
三、深入理解原型
其實,上面的一二兩點,都是總結,你有想過,為什麼函式會存在三種角色嗎? 其實,想要理解函式的三種角色,就必須深入探索一個完整的原型鏈的工作方式。
先看一個例子,幫助我們理解函式的三種角色
function Fn() { this.x = 100; } Fn.prototype.getX = function () { console.log(this.x); }; var f = new Fn; console.log(f instanceof Fn); //->true console.log(f instanceof Object); //->true
簡單說明一下上面的程式碼,使用的原型繼承的方式,首先宣告定義一個Fn函式類,然後再Fn函式類的原型上新增一個公有的方法getX,之後,使用new關鍵字建立一個Fn函式類的一個例項,從而f繼承了Fn的私有+公有的方法。
很明顯,f instanceof Fn 是true的,因為f是通過new Fn來創建出來的,但是為什麼f instanceof Object 也是為true呢? 要回答這個問題,得理解下面的話。
1)Function是瀏覽器內建的函式類(Function也是物件),所有的函式都是Function類的一個例項
2)Object是瀏覽器內建的物件類,所有物件都是Object類的一個例項
3)所有例項,都是物件資料型別
4)根據1、2兩點,可以知道,上面定義的Fn函式,是Function類的一個例項,那麼Fn函式其實就是一個例項,根據第3點,Fn函式就是一個物件資料型別;
同理,因為Object物件類是一個類,類其實也是就函式,所以Object物件類是Function類的一個例項。
5)根據第3、4點,可以知道,內建類Function、內建類Object、函式Fn它們都是物件,那麼是物件會擁有__proto__屬性,所以它們都擁有__proto__屬性。
四、第三點中程式碼的完整原型鏈
值得注意的是,上圖中,Function.prototype 其實並不是物件,是函式資料型別,非常坑爹!其實它叫做anonymous(匿名函式) ,沒有實際意義的一個函式,但是操作起來跟物件一模一樣。
列印Function.prototype,樣子如下所示:
五、總結
函式在整個JS中是最複雜也是最重要的知識,一個函式存在多面性,而且相互不衝突:
-> “函式”:它本身是一個函式,執行的時候形成私有作用域(閉包),形參賦值、預解釋、程式碼執行、執行完後棧記憶體銷燬/不銷燬;
-> “類”: 它有自己的例項,也有一個叫做prototoype屬性是自己的原型;
-> “普通物件”:和var obj = {} 中的obj一樣,就是一個普通的物件,它作為物件有自己的私有屬性,也可以通過__proto__找到Function.ptototype。