咱們都生活在主觀的世界裏,但真實世界倒是個複雜系統。 javascript
對於一個非線性系統來講,用任何線性思惟去理解都會所偏頗。java
用《失控》的觀點來講,對於非線性系統,你只有運行起來才知道它具體會是什麼。git
話雖如此! github
《好好學習》一書中,一個重要假設就是:複雜現象背後都是由幾個簡單的規律所主導的。編程
我的認爲 JS
也是屬於這種情形的。設計模式
JavaScript(ES3)爲啥是這樣? 數組
也許只有 JavaScript
的做者才清楚吧,畢竟他只花了 10 天就發明了這個強大的存在。瀏覽器
本文不想去回顧歷史,也不是去還原做者的創做思路。而是以嘗試以本身的思路去理解 JS
。 閉包
但願本篇文章,能給一些初學者點啓發,尤爲對於以類爲主導的語言出身者(好比 Java
使用者)。app
除此以外,本文再無所求。
JS 的發明者 Brendan Eich
說這門語言主要有兩大特性:一等函數和對象原型。
一等函數,我認爲這個纔是 JS
最爲核心的東西。
以函數是一等公民爲出發點,就能解釋 JS
爲啥有那些難點。
什麼是一等函數呢(
First-class function
)?
函數是一等公民是說,函數被設計成了「值」。
值能幹什麼它就能幹什麼。
值能夠存進某個變量,或者某個結構,那麼函數表達式聲明就是理所固然的。
值除了能夠被賦予某個變量,也能夠單獨存在。好比 "x"
,因此函數就能夠是匿名的。
值能夠做爲參數傳入函數,函數也能夠,所以在 JS
中回調的實現顯得那麼天然。
值能夠做爲返回值,函數也能夠。這一點就很是重要了,由於這直接致使閉包這一機制必須存在。
值能夠隨時用,也就是動態的,函數也可隨時用,所以匿名函數自執行,我以爲很是合理。
除了函數以外還有其餘一等公民嗎?
函數是,數組是,對象也是。他們都體現了「值」的特性。
所以在 JS
中,有兩句話,特別有名:
JavaScript
的一切都是對象,
JavaScript
的一切也都是值。
兩者在 JS
中實現了對立統一。
知道這些後,也許你對不少東西就再也不迷糊了,反而會越合計越合理。
下面將舉例來講明,爲啥 JS
中有那麼多「奇怪」的東東。
好比:
setTimeout(function() {}, 1000);
複製代碼
還記得當初學習JS時,感受很迷糊。函數居然能當參數。
setTimeout(later, 1000);
複製代碼
你還記得,不少書都告訴你不要寫成 later()
嗎?
後來我認識到函數是一等公民後,學習 es5
時,也就再也不迷糊:
[1, 2, 3].map(function(value) { return value * value; });
複製代碼
函數能當參數傳遞,JQuery
對它的使用能夠說達到了極致。具體應該不用舉例了吧。
函數做爲返回值這塊兒,直接致使了閉包的存在。
固然這不是充要條件,但能夠做爲一個理解閉包出發點。
咱們知道,函數基本做用就是做爲一個可複用代碼的封裝,有輸入,有輸出。
好比:
function sum(a, b) {
return a + b;
};
複製代碼
可是當函數居然還能返回函數時,有趣的事情就發生了:
function sumCreater(x) {
return function(y) {
return x + y;
};
}
var sum10 = sumCreater(10);
sum10(12)// 22
複製代碼
函數能返回函數是好事,可是被返回的函數最起碼應該能運行才行。
你要運行的話,內部引用的外部變量天然而然就該能被找到。
否則怎麼辦?難道你想讓瀏覽器直接報錯?這個機制就是閉包。
這裏具體不展開,由於我只是在搭建世界觀。
關於函數能當返回值,能當參數。這就涉及了高級函數概念。
所以也有了不少函數式編程的基礎知識,好比柯里化,偏函數,函數組合等等。
(function() {})();
複製代碼
函數是能夠調用的,這沒啥可說的。那麼聲明個函數,直接運行一下,這應該一點也不奇怪。
一等函數是核心,之因此這麼說,也由於它對 JS
支持面向對象編程機制起到了關鍵做用。
一個對象,應該有屬性,有方法。由於函數是值,因此下面的代碼,應該很親切:
var object = {
say: function() {
console.log('hello world!');
};
};
複製代碼
由於對象的方法,要常常用到本身的屬性,this
是應該必須有的,否則用什麼指代當前這個對象自己呢?
var object = {
name: 'laoyao',
say: function() {
console.log(this.name);
};
};
複製代碼
那麼這裏就涉及到一個問題,this
是在函數語義下的存在。
假如直接調用函數,this
指向什麼呢?
我又能夠不能夠動態修改一個函數 this
指向呢?
以上就是 JS
的 this
指向問題。
你有個東西,我以爲好,想爲我所用。
辦法1,我去偷或者借,JS
中能夠 call
和 apply
。
辦法2,我 copy
一份,這就是 JS 中的混入(mixin
)。
辦法3,我認你爲爹。你都是我爹了,我用你的東西,還不行嗎?這是 Java
的世界觀。
辦法4,我當你是個人智囊。我解決不了問題,找你來想辦法。
辦法3和辦法4,就是兩種複用思想,一個是繼承,一個是委託。
而 JS
的 prototype
,就是委託的機制。請參考《你不知道的JavaScript》上卷。
我總不能每使用一個類似的對象時,就聲明一個吧。
所以必須有種方式,經過相似「模板」的形式來作。這就是 new
的做用。
而 JS
中的 new
與 Java
中的 new
,表層含義是同樣的,都是用於生成對象實例。
然而,JS
中的 new
在我看來只是封裝了對象間的委託關係罷了。
上面總總就是我對 JS
的世界觀。
爲啥要用「世界觀」這個詞語呢?由於我最近在看一本書,《世界觀》。
書中說,對世界的見解和信念,雖是一片一片的,倒是相互聯繫的,是能整合到一塊兒去的,從而構成完整的拼圖。
好比你相信世界是以地球爲中心的,那麼就能夠解釋物體爲啥會向下落。
一樣,假如你相信萬有引力,也能夠解釋物體爲什麼向下落。
若是採用某種方式,能讓本身以爲一些知識是天然而然,就本該是那樣的,也許會更好地對其吸取和利用。
固然,採用不一樣語言世界觀,就會造成不一樣的編程範式。
就像有人喜歡使用面向對象編程來使用 JS,而有人喜歡函數式編程那樣。
說到編程範式,提下設計模式。
面向對象有面向對象的設計模式,函數式編程也有本身的設計模式。
解決問題的動機是同樣的,但在 Java
中的複雜形式,而在 JS
中,卻簡單了不少。
這跟 JS
函數是一等對象是密不可分的。
提到了設計模式,最後說句 JS
的原型繼承方式,就是合成複用模式。
每每「有一個」比「是一個」的方式更好。
因此,你想有錢,不必定須要是富二代,你能夠傍個大款。
因此,你想認識世界,不必定須要本身變成旅行者,你能夠上知乎問「周遊世界是一種怎樣的體驗?」。
因此,你想幹成大事,不必定須要本身能力多強,你只要有幾個聰明人爲你所用就好了。
因此,借刀殺人、狐假虎威以及借雞下蛋都是聰明人最愛使用的方法。
。。。
但願你也能找到一種方式,讓你產生 JS
就該是這樣的一種錯覺。
本文完。