數據類型能夠說是編程語言的基石,重要性不言而喻。那麼如今就從數據類型開始,打破你的思惟認知,作一個充滿想象力的FEE
。針對上篇的一些偏激評論,我想強調的一點是:我寫的文章,並非給那些偏激到說髒話的人看的,請尊重每一位爲前端貢獻微薄力量的Blogger
。前端
好像,我這標題起的也太秀了,會不會被打😂。java
這篇能夠算是 前端獵奇系列中的 探索 Python 來反補 JavaScript 的中篇。 若是沒有看過上篇文章的,能夠去個人專欄裏讀讀上篇,在知識上沒有啥關聯的地方,相對獨立。基本是我在學習PY
的時候,學到某一個地方,忽然會想到JS
在這一方面是如何表現的,而後隨着思考,真的會有很多收穫吧。node
有句話說的好,掌握數據類型是學習一門語言的基礎。咱們從這句話中能夠看出,掌握好數據類型是有多麼重要。你曾經是否是有想過JS
的數據類型爲何會是這樣,爲何要有null
、undefined
。也許你有過疑問,可是疑問觸發後,簡簡單單的探尋後,就把疑問扔到回調函數裏了,這一扔就是到現在。如今我將從PY
的角度來反補JS
,經過PY
去看清JS
的數據類型,看清編程語言的一些規律。now go!chrome
JS
的數據類型分爲值類型和引用類型:編程
PY
的數據類型分爲數值類型、序列類型、Set類型、字典類型:segmentfault
JS
的Set相同)JS
的Map相同)如今咱們看一下PY
和JS
的數據類型,這裏我不闡述具體是什麼,我只是總結一下,當我學習到這的時候,我對JS的數據類型有了什麼樣新的理解。如今,你會發現幾個頗有趣的地方,請看以下:數組
這和PY
的Set
、Dictionary
不謀而合,可是ES6
規範的制定者,沒有選擇使用Dictionary
做爲鍵值對的類名稱,而選擇了使用Map
做爲鍵值對的類名稱。而Map
正是Java
的鍵值對的類名稱。因此給個人感受就是,JS在吸取不少語言的優秀的特性,我我的認爲,命名成Map
要比Dictionary
好,畢竟少寫7
個字符呢😂。瀏覽器
就這樣就結束了嗎?No,咱們再看上面兩種類型,首先注意PY
的List
和JS
的Array
是相同的,都是能夠動態進行修改的。可是不少FEE
,由於掌握的知識不夠寬泛,致使了對不少事情不能理解的很透徹。好比,咱們的思惟中就是這樣一種固定的模式:數組是能夠動態修改的,數組就是數組類型。。我我的建議,FEE
必定不能將本身的思惟束縛在某個維度裏。這真的會阻礙你 開啓那種瞬間頓悟的能力。前端工程師
若是你瞭解了PY
或者其餘語言,你會發現其實JS
的數組,其在編程語言裏面,只能算是List
類型,屬於序列類型的一種。並且很重要的是,JS
的Array
是動態的,長度不固定,瞭解過Java
的同窗應該知道,在Java
中,數組是分爲Array
和ArrayList
,Aarry
是長度固定的,ArrayList
是長度能夠動態擴展的。因此JS
的Array
其實只是編程語言 的Array
中的一種。若是你知道這些,我以爲這對去深入理解JS
的數據類型將有很大的幫助。雖然JS
對一些知識點進行了簡化,可是做爲一個合格的計算機工程師,咱們不能習慣的接受簡化的知識點,必定要去多維度理解和 掌握簡化的知識點。瞭解其背後的世界,也是很是奇光異彩的。編程語言
你會發現JS
的String
是被歸類爲數值類型,而PY
的String
是被歸類爲序列類型。其實我我的更傾向於把JS
的String
歸爲序列類型,爲何這麼說呢,由於JS
的字符串自己就帶有不少屬性和方法,既然有方法和屬性,也就意味着至少是個對象吧,也就是隱式執行了new String
。字符串對象能夠經過方法和屬性來操做本身的字符序列。因此這被歸類爲數值類型的話,我我的認爲是不科學的,而PY
就分的很清楚。
null 和 undefined 的爭論就在此結束吧。
可能一開始會對null
和undefined
比較陌生,可能會有那麼一刻,你懷疑過JS
的null
和undfined
爲何會被單獨做爲數據類型,可是過了那一刻,你就默許其是一個語言設計規則了。可是我想說的是,語言設計規則也是人設計的,是人設計的就應該多一份懷疑,沒必要把設計語言的人當作神同樣。編程語言那麼多,哪有那麼多神。網上有不少好文章介紹JS
的undefined
和null
的,也都說了有多坑。想深刻理解有多坑的能夠自行百度谷歌。我也不重複造解釋了,好比,undefined
竟然不是保留字,也是夠神奇的,看了下博客,有篇解釋的很不錯,能夠瞅瞅爲何undefined能夠被賦值,而null不能夠?。寫博客的時候,並非一味的寫本身的東西,有時候別人總結好的東西,在我寫博客過程當中,也能帶給我不少靈感和收穫。這也是算是和站在巨人的肩膀上是一個道理吧。
不過我仍是有點我的獨特的見解的。並且我認爲個人見解要比網上絕大多數的看法要更加深入(不要臉)。我不想說undefined
有多坑,我只想去探究和理解undefined
的本質。掌握了本質後,坑不坑也就不重要了。我我的認爲,JS
的undefined
是一種爲了處理其餘問題而強行作出的一種折中方案,且聽我娓娓道來。
既然PY
和JS
都是解釋性語言,那麼爲何PY
能夠不依賴undefined
,只須要使用None
就能夠了呢? 我寫一個簡單的例子,能夠從我下面的分析中,找到一些更深層的真相,找到設計undefined
真正的緣由。代碼以下:
let x
console.log(x)
複製代碼
# coding=utf-8
print(x)
複製代碼
咱們來看運行結果:
從圖中會發現,JS
沒有報錯,可是PY
報錯了,到底是什麼緣由? 這裏中斷一下,咱們來看下面這個截圖,是java的一段代碼的運行結果:
圖中能夠看出,在Java中,能夠聲明變量,但不賦值,而後不去調用此變量,程序是不報錯的,可是在PY中,請看下面截圖:
咱們發現,咱們聲明瞭,也沒有去調用它,程序仍是報錯了。是爲何呢?
爲何在Java
,C++
,C
語言中,能夠聲明變量,而不用賦值,而且不會報錯。而在PY
中會報錯呢,而在JS
中是undefined
呢?其實仔細一想,會恍然大悟,一個很是關鍵的一點就是:
Java
、C++
,C
是強類型語言,在聲明的時候,就已經肯定了數據類型。因此就算不去賦值,Java
、C++
等也會根據聲明的數據類型,設置一個默認的數據類型的值。可是這裏注意一點,若是整個程序執行完,在只聲明,卻沒有賦值的狀況下,去輸出或者調用該變量,程序會報錯的。爲何會報錯呢,是由於此變量的地址是系統隨機生成的,並不在此程序內的地址範圍內,也就是說此變量的地址多是指向其餘程序的地址,在這種狀況下,若是去調用該地址,那麼可能會出現很大的危險性,好比你調用了其它很重要的東西。這裏我以爲能夠把它理解爲遊離的指針,雖然這樣形容很差,可是很形象,遊離的指針是很危險的東西。有多危險,哈哈哈,本身體會✧(≖ ◡ ≖✿)。
中斷結束,繼續PS
,從上面的敘述知道了Java
等語言是強類型語言。可是咱們知道而PY
和JS
是腳本語言,屬於弱類型語言,而弱類型語言的特色就是:在聲明變量的時候,不須要指定數據類型,這樣的好處就是一個變量能夠指向萬物,缺點是性能差一些,須要讓編譯器在賦值的時候本身去作判斷。請緊跟着個人腳步,咱們來看下面這段代碼:
let x
console.log(x)
複製代碼
能夠看到,x
是JS
聲明的變量,因爲腳本語言是動態的,因此這個變量x
能夠指向萬物,那麼若是直接使用x
,而不讓其報錯的話,該怎麼作呢。
一個原則必定不能忘,就是不賦值的話,調用必定會報錯,OK,那就賦值,給一個默認值,那麼這個默認值用什麼來表示呢,指向萬物的話,那這類型的可能性有好幾種,若是使用null
來表示的話,因爲null
表明空對象,這裏說一個很關鍵的點,就是,爲何其餘語言中好比Java
,C++
,他們對於空,都是使用null
來表明一個空對象的?
其實最本質的緣由仍是由於他們是強類型語言,必須在變量前面聲明數據類型,對於值類型,他們系統能夠自動給一個默認值。因此在強類型語言中的null
,其做用只是給引用類型用的。而到了弱類型語言中,好比PY
,JS
,咱們看PY
,因爲PY
老哥不想使用undefied
,也只想用一個null
。那麼天然而然的結果就是:直接不容許在未賦值以前,直接調用聲明的變量
,只要調直接提示報錯,那麼你可能會有疑問了,爲何PY語言中,連只聲明變量,不去調用它,程序都會報錯呢。其實我我的以爲緣由是由於弱類型語言的數據類型不肯定致使的,編譯器沒法去給一個默認值,也就意味着不肯定因素增長,既然不肯定,那PY
的作法就是直接使其報錯。經過編譯器報錯來顯式讓開發者去遵循編碼規則。
而小可愛JS
就不同了,因爲設計者就是不想使其報錯,想容許聲明,而且能夠在未賦值的時候還能夠直接調用而不報錯。因此也就意味着他要給聲明的變量賦一個默認值,怎麼賦值呢?這估計也是困擾了設計者良久,下面我舉一個很簡單易懂的例子,請看下面代碼:
let x;
let y = [1,2,3]
console.log(x, y[3])
複製代碼
從代碼能夠看出,若是想不報錯,有幾種可能:
第一種: 按照其餘語言的規範,只保留一個空值null
,ok,繼續往下推導,因爲JS
是弱類型,變量指向萬物,因此確定只能給全部聲明但未賦值的變量設置null
爲默認值了。可是這樣的話,問題來了。
看第三行代碼,其實y[3]
也是聲明未賦值的變量
,是否是有點不相信,以爲超出認知了。沒事,先繼續往下看,既然y[3]
也是未賦值的變量
,那把y[3]
的默認值也設置爲null
嗎?很明顯,不合理。
由於y[3]
多是各類類型,若是直接都設置爲null
。那用戶直接打印y[3]
,而後蹦出來一個null
,仍是object
類型,豈不要炸?因此到這裏,我會慢慢發現,其實JS
中的null
和undefined
是徹底不一樣的兩碼事,很容易去區分。
綜上,我猜一下JS
做者的腦洞應該是這樣的,既然我想讓調用聲明未賦值的變量不報錯,那ojbk
。不是弱語言麼,不是指向萬物嗎?那要來就來刺激點,我就單獨設置一個數據類型,名爲undefined
。專門用來counter
指向萬物的聲明卻未賦值的變量。哈哈哈哈,是否是很刺激😂。
看下面代碼
let x
let y = [1,2,3]
console.log(x, y[3])
複製代碼
你會發現x
和y[3]
都是undefined
。咱們來透過現象看本質,本質上就是聲明瞭,可是未賦值。爲何能夠這麼說,難道y[3]
,也是聲明瞭,但未賦值嗎?我能夠明確告訴你,是的,沒毛病。你可能不相信我說的話,下面我在白板上畫一個圖就頓悟了。。請看圖:
圖中能夠看到,其實數組的每個下標也是在棧裏進行聲明的。和用let x
進行聲明的操做是同樣的。let x
的聲明以下圖:
因此是否是發現其實undefined
也就那麼回事吧。通常來講,若是某一個知識點越繞人,那咱們就應該從更底層的角度去看清這個知識點。只要你真的是從一個更加深入和底層的角度去看待undefined
,其實 just so so 啦。對了,null
我也順帶解釋了,只不過沒有重點關注,可是整篇下來,其實null
是什麼,也差很少一清二楚了。總之null
和undefined
就是徹底不一樣的兩碼事。
從JS
和PY
的數據類型,咱們能夠看出,PY
在設計數據類型的時候,明顯考慮的不少,或者說,PY語言在被創造的時候,其數據類型的設計是比較規範的。而咱們去看JS
,會發現,有不少坑,感受是當初爲了簡化知識點難度,而留下了不少坑。雖然我沒有經歷過IE
時代的前端,但如今也能深入體會到前端工程師的不容易。之前還有同行說前端很簡單啊,如今也有,我都遇到過好幾回這種人了:
我:我是前端開發。
人家:噢,我知道了,就是寫網頁的對吧。。。
我內心os:對你個錘子。。
FEE
們都是從坑裏一步步爬上來的,真的不容易。總之,如今的前端正在一步步走上規範,走上體面。。。
PY
中如何處理動態參數的呢,其實PY
是經過元組或者字典來處理動態參數的,代碼以下,這裏只寫使用 元組 實現動態參數的代碼
# coding=utf-8
def add(x, *tupleName):
print(x, tupleName)
add('hello', 'godkun', '大王叫我來巡山')
複製代碼
執行結果圖以下:
咱們再看JS
是如何實現的
function fun(a, ...tupleName) {
console.log(a, tupleName)
}
fun('hello', 'godkun', '大王叫我來巡山')
複製代碼
執行結果圖以下:
看上面兩種方式,看完你應該就明白了,ES6
增長展開符的緣由是什麼,以及爲何要設計成這個樣子。使用...
做爲標記。同時爲何要將全部可變參數放在一個數組裏面。
其實語言都是有相同性的,尤爲對於JS
語言來講,採納了不少語言的優勢,這對於咱們前端來講,是一個很大的優點,若是平時善於去這樣比較和反補,我我的以爲,FEE
去承擔其餘開發崗位,也是徹底能Hold
住的。
當我寫下這段代碼:
function a(a, b, c) {
console.log(arguments);
console.log({ 0: "1", 1: "2" });
console.log([1, 2, 3]);
}
a(1, 2, 3);
複製代碼
第一種狀況:我在node.js
環境運行:結果如圖所示:
第二種狀況:我在chrome
瀏覽器下執行這段代碼,結果如圖所示:
第三種狀況:我在IE
瀏覽器下執行這段代碼,結果如圖所示:
上面第二種狀況,你會發如今chrome
瀏覽器下,輸出的結果形式爲:
Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
0: 1
1: 2
2: 3
callee: ƒ a(a,b,c)
length: 3
Symbol(Symbol.iterator): ƒ values()
__proto__: Object
複製代碼
我靠,什麼鬼。竟然把arguments
寫成了數組的形式:
[1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
可是 __proto__
仍是 Object
。嚇的我趕忙試了下面這段代碼,代碼如圖所示:
靠,還果然返回了長度。。。可是爲何__proto__
是Object
。。。。
不行,我又看了IE瀏覽器和node.js
環境下的結果,都是相同的結果,使用{}
表示類對象數組
{0: 1, 1: 2, 2: 3, callee: function a(a,b,c){}, length: 3}
複製代碼
我陷入了沉思。。。。
不知道是chrome
開發者故意這樣設計的,仍是寫錯了。。小老弟,你怎麼回事? chrome
會弄錯? 本着上帝也不是萬能的理念,我打開了個人腦洞。
chrome
瀏覽器既然不按照{}
這種寫法,直接將arguments
寫成[]
,使其直接支持數組操做,同時,其原型又繼續是對象原型。仔細看會發現又加了一行
Symbol(Symbol.iterator): ƒ values()
。
這樣作的目的是什麼,爲何要這樣設計?搜了blog
,然而沒搜到。。。這一連串的疑問,讓我再次陷入了沉思。。。
思考了一會,動筆畫了畫,發現好像能夠找到理由解釋了。我以爲能夠這麼解釋:
chrome
想讓類數組對象這種不倫不類的東西從谷歌瀏覽器中消失。因此下面這種輸出結果
{0: 1, 1: 2, 2: 3, callee: function a(a,b,c){}, length: 3}
複製代碼
就一去不復返了,那麼若是不這樣寫,用什麼方法去替代它呢。答案就是寫一個原型鏈繼承爲對象類型的數組,同時給繼承對象類型的數組(其仍是對象,不是數組) 增長Symbol.iterator
屬性,使其能夠for of
。
爲何要這樣作呢,由於一些內置類型自帶迭代器行爲,好比String
、Array
、Set
、Map
,可是Object
是不帶迭代器的,也就意味着咱們能夠推斷出,若是從chrome
瀏覽器的那種寫法的表面上分析,假定arguments
是Array
,那麼就徹底不必增長Symbol.iterator
,因此矛盾,因此能夠得出,arguments
仍是對象,而對象是不帶迭代器的。因此要給形式爲 []
的arguments
增長 Symbol.iterator
。使其具備迭代器功能。從而可使用for of
。從而完成了 [1,2,3]
到 {'0':1, '1':2, '2':3}
的轉變
因此:上述答案被證實爲正確。
固然,也多是:
有理有據的胡謅。。。
備註:
文末的可愛聲明: 若是轉發或者引用,請貼上原文連接,尊重一下勞動成果😂。文章可能 (確定) 有一些錯誤,歡迎評論指出,也歡迎一塊兒討論。文章可能寫的不夠好,還請多多包涵。人生苦短,我學前端,多一點貢獻,多一分開心,歡迎關注,後續更加精彩哦~
小夥伴以爲我寫得還不錯的話,就點個贊 以茲鼓勵 一下吧😊。