對於上面這種用var的聲明方式,不管x的默認值爲何,只要形參中出現了默認值,zzz都會被看成塊級做用域中的值。git
這是我偶然間遇到的一個問題,起初我認爲這是chrome的bug,我將個人想法請教了一位朋友,他告訴我說這不是bug,並讓我先看看這篇params default value & params environment & TDZ程序員
看完後我將個人想法進一步告訴了他,個人想法能夠用下面這5張圖來歸納。github
我認爲這是chrome的bug,若是說是block,那麼出了這個塊就不應被訪問到,可是事實是能訪問到算法
並且從自己的語法來說,他也不該該是block,而是function scope。chrome
他回答說你沒看懂,並告知我沒看規範是很難理解,那麼沒辦法了,讀讀規範吧,對規範已經不陌生了,在個人前兩篇文章中,已經引用了規範中的不少內容。下面咱們先來解釋下規範中對於這一問題相關的解釋,而後根據這些去解釋咱們遇到的這一問題。segmentfault
注:如下爲ES6規範,ES6規範,ES6規範,重要的事情說三遍,不是ES5噢~瀏覽器
一個詞法環境是一種規範的類型,用做定義基於JS代碼的嵌套詞法結構中標識符與變量或者函數間的關聯。一個詞法環境包括一個Environment Records(即做用域記錄,如下咱們也簡稱ER)和一個可能爲null的指向外部詞法環境的引用。閉包
一般一個詞法環境與JS代碼一些特殊的語法結構想關聯,如函數聲明,塊級語句,或者try語句中的catch從句。當每次這些代碼被解析的時候,都會建立一個新的詞法環境。app
一個ER記錄了與它關聯的詞法環境的做用域中的標識符綁定。因此稱之爲做詞法環境的ER。函數
外部的詞法環境引用用做模擬邏輯上的詞法環境嵌套。一個詞法環境的外部引用也是一個引用,它指向圍繞或者說包括當前這個詞法環境的詞法環境。固然,外部的詞法環境又有它本身的外部詞法環境,這就是咱們常說的做用域鏈。
一個詞法環境可能做爲多個內部詞法環境共同的外部詞法環境。例如,一個函數聲明中有兩個內嵌的函數聲明。一個語句塊中有兩個內嵌的語句塊。
一個全局環境是特殊的詞法環境,它沒有外部詞法環境,它的外部詞法環境引用爲null。一個全局環境的ER也許會被用標識符綁定進行預填充,包含一些相關的全局對象,它的屬性提供一些全局環境下的標識符綁定,即內置對象,不一樣的JS宿主環境,內置對象不一樣。
這個全局對象就是全局環境下this的值。當JS代碼運行的時候,其餘的屬性也許會被加入到全局對象中,最初的屬性可能會被修改。
一個模塊環境是一個詞法環境,它包括對於一個模塊頂部聲明的綁定。它也包括對於經過模塊顯式導入(經過import)的模塊的綁定。一個模塊環境的外部環境爲全局環境。
調用一個函數的時候,一個函數環境也是一個詞法環境,與函數對象想對應。一個函數環境也許會創建一個新的this綁定(好比構造函數,對象中的函數),注意這裏的也許二字,由於this只有調用時才能肯定。一個函數環境也會捕獲必要的狀態以支持調用父級方法。
詞法環境和ER值是純粹的規範,它們不須要對應於任何特定的ECMAScript實現。在ECMAScript程序中不可能直接訪問或者操做它們。
在規範中,有兩種類型的ER,聲明式ER(declarative Environment Records)和對象式ER(object Environment Records)。
聲明式ER(declarative Environment Records)被用做定義ECMAScript(如下簡稱ES)語言中語法元素的做用,例如函數聲明,變量聲明,以及catch語句中把綁定的標識符與ES語言中的值(Undefined, Null, Boolean, String, Symbol,Number, and Object中的一種,如下簡稱ES合法值)聯繫在一塊兒。
對象式ER(object Environment Records)被用做定義例如with語句這類把綁定的標識符與某些對象聯繫起來的ES元素。
全局ER(Global Environment Records)和函數ER(function Environment Records)是專門用做全局腳本聲明和函數內的頂部聲明(也就是咱們常說的聲明提高)。
爲了規範ER的值是Record規範類型而且可以存在於簡單的面向對象層次結構中。能夠認爲ER是一個抽象類,他有三個子類-聲明式ER,對象式ER,全局ER。函數ER和模塊ER(module Environment Records)是聲明式ER的子類。ER這個抽象類包含許多抽象方法(見下表),這些抽象方法在不一樣的子類中有不一樣的實現(既然是抽象方法,那麼這是必然的)
表1:ER中的抽象方法
Method | Purpose |
---|---|
HasBinding(N) | 判斷ER中是否綁定有N(便是否有標識符N),有返回true,不然返回false |
CreateMutableBinding(N, D) | 在ER中建立一個新的未初始化的且可變的綁定(能夠理解爲聲明一個變量),N爲標識符名,D是可選參數,若是爲true,這個綁定隨後可能會被刪除。 |
CreateImmutableBinding(N, S) | 在ER中建立一個新的未初始化的且不可變的綁定,N爲標識符名。若是S爲true,不管是否在嚴格模式下,在它初始化以前嘗試去訪問它的值或者在他初始化後設置它的值都會拋出異常(就是咱們用到的const)。S是可選參數,默認爲false。 |
InitializeBinding(N,V) | 設置ER中已經存在可是未初始化的綁定的值。N爲標識符名,V爲ES合法值。 |
SetMutableBinding(N,V, S) | 設置ER中已經存在可是未初始化的綁定的值。N爲標識符名,V爲ES合法值。S爲一個boolean類型標誌,若是爲true而且沒法設置成你傳入的值,將拋出一個TypeError錯誤。 |
GetBindingValue(N,S) | 返回一個ER中已經存在的綁定。N爲標識符名。S被用做識別原始引用是否在嚴格模式中或者須要使用嚴格模式語義。若是S爲true且綁定不存在,將拋出一個ReferenceError異常。若是綁定存在可是未初始化,不管S爲什麼值,一個ReferenceError異常將被拋出。 |
DeleteBinding(N) | 從ER中刪除一個綁定。N爲標識符名,若是N存在,刪除並返回true。若是N存在可是不能被刪除返回false。若是N不存在,返回true。 |
HasThisBinding() | 判斷ER是否綁定了this。(就是咱們經常使用的call和apply)。若是是返回true,不然返回false。 |
HasSuperBinding() | 判斷是否有父類方法綁定。若是是返回true,不然返回false。 |
WithBaseObject () | 若是ER與with語句有關聯,返回with的對象。不然,返回undefined |
每一個聲明式ER都與一個做用域想關聯,這個做用域包含var,const,let,class,module,import或者function聲明。一個聲明式ER綁定它的做用域中定義的標識符的集合。
有了上面的基本解釋,咱們下面來看與提問有關的地方:
請記住這裏的func和argumentsList,在後面描述過程的時候咱們會屢次提到
當爲瞭解析一個JS函數建議執行上下文的時候,一個新的函數ER就被創建,而且綁定這個ER中每一個實例化了的形參(這裏的實例化應該是指在執行函數的時候,形參纔能有值,有值以後就表明實例化了)。同時在函數體中的每一個聲明也被實例化了。
若是函數的形參不包含任何默認值,那麼函數體內的聲明將與形參在同一ER中實例化。
若是形參有設置默認值,第二個ER就被創建,他針對的是函數體內的聲明(咱們能夠形象的理解爲這是一個除了函數做用域和塊級做用域以外的"第三做用域")。形參和自己的函數聲明是函數聲明實例化的一部分。全部其餘的聲明在解析函數體的纔會被實例化。
其實到這裏,咱們就已經能解釋咱們提出的問題了,用var聲明的變量在chrome中顯示爲Block,並非表明他爲塊級做用域中的值,而僅僅是爲了區分形參的ER和函數體的ER,形參的ER中的變量只能讀取形參ER中的變量或者函數外的變量,而函數體內的變量能夠讀取函數體內,形參,外部的變量。這裏摘抄下上面提到的文章中的代碼片斷:
let y =1; function foo(x = function(){console.log(y)},y=2) { x(); // 2 var y = 3; // if use let, then throw error: y is already declared, which is much more clear. console.log(y); //3 x(); // 2 } foo(); console.log(y); //1
這即是咱們chrome爲何要區分形參的ER和函數體的ER的緣由,是爲了讓咱們看得更加清晰。
問題雖然解決了,可是規範卻還意猶未盡,有興趣的同窗能夠接着往下將這規則中這一節的內容看完。
函數聲明實例化按照以下過程進行。其中func
爲函數對象,argumentsList
爲參數列表。
1. Let calleeContext
做爲運行時上下文/運行時環境(Execution Contexts,見下)
Execution Contexts(原文爲8.3節內容,可是這裏提到了,因此咱們在這裏就一併解釋了):
一個運行時上下文或者說運行時環境是用來跟蹤一個ECMAScript實現(注意ES實現不止JS一種)的代碼的運行時解析。在運行時的任意時間點,最多隻存在一個運行時上下文,即當前執行的代碼。
一個棧被用做跟蹤運行時環境,運行時環境老是指向棧頂的元素(也就是咱們常說的調用棧,chrome調試時的call stack)。不管什麼時候,只要運行時環境從當前運行的代碼轉移到非當前運行時環境的代碼,就會建立一個新的運行時環境,並將這個新的運行時環境push到棧頂,成爲當前的運行時環境。
爲了跟蹤代碼的執行過程,一個運行時環境包含實現具體的狀態是有必要的。每個運行時環境都至少有下表列出的這幾種元素。
Component Purpose code evaluation state 包含與運行時環境相關的代碼所需的任何狀態,如執行中,暫停,繼續解析 Function 若是運行時環境正在解析一個函數對象,那麼這個值就爲那個函數對象。若是正在解析一個腳本(script)或者模塊(module),那麼這個值爲null Realm(域) 來自相關代碼能夠訪問的ECMAScript resources的域。注:ECMAScript resources包含客戶端ECMAScript,ECMAScript核心標準庫,擴展自ECMAScript核心標準庫的服務端ECMAScript。域包括全局對象和內置對象 運行時環境的代碼解析可能會被各類各樣的狀況打斷而致使暫停或者說掛起。一旦運行時環境切換到另外一個不一樣的運行時環境,那麼這個不一樣的環境就可能成爲當前運行時環境,並開始解析代碼。一段時間事後,一個暫停的執行環境也許會成爲運行時環境而且從以前的暫停點繼續解析代碼。運行時環境的這種來回切換的狀態是經過類棧結構來過渡的。然而,一些ES特性須要非棧的過渡。
運行時環境的Realm的值也被稱做當前域。運行時環境的Function的值也被成爲活動函數對象。
ECMAScript的運行時環境有額外的state元素(見下表)
Component Purpose LexicalEnvironment 標記用做解析當前運行時環境中代碼裏的標識符引用的詞法環境 VariableEnvironment 標記在當前運行時環境中詞法環境的ER包括var聲明建立的綁定的詞法環境 上表中的詞法環境(LexicalEnvironment)和變量環境(VariableEnvironment),在一個運行時環境中老是表現爲詞法環境。當一個運行時環境被建立的時候,它的詞法環境和變量環境初始化爲相同的值。
能夠參考下stackoverflow上的解釋1以及stackoverflow上的解釋2:
// VariableEnvironment (global) = { __outer__: null } // LexicalEnvironment = VariableEnvironment (global) (function foo() { // VariableEnvironment (A) = { x: undefined, __outer__: global } // LexicalEnvironment = VariableEnvironment (A) var x; (function bar(){ // VariableEnvironment (B) = { y: undefined, __outer__: A } // LexicalEnvironment = VariableEnvironment (B) var y; x = 2; // VariableEnvironment (A) = { x: 2, __outer__: global } // LexicalEnvironment is still the same as VariableEnvironment (B) })(); })();對於構造器的運行時上下文,有額外的的state元素(見下表)
Component Purpose Generator 當前運行時環境正在解析的構造器對象 在大多數狀況下,只有當前運行時環境(即運行時環境棧的棧頂元素)直接被規範中的算法操做。
2. Let env
做爲calleeContext(當前的運行時上下文,也就是運行時上下文的棧頂元素)的詞法環境(LexicalEnvironment)
3. Let envRec
做爲env的ER
4. Let code
等於[[ECMAScriptCode]]
這個func的內嵌屬性的值(內嵌屬性(兩個中括號包裹的屬性)並非ES的一部分,由ES的具體實現來定義,它們純粹是爲了展現,更重要的一點,它們具備多態性。下面再看到中括號就再也不解釋內嵌屬性了)
[[ECMAScriptCode]]
:類型爲Node。值爲源代碼文件解析後的函數體,即函數對象有一個屬性[[ECMAScriptCode]]能夠指向自身的函數體。
5. Let strict
等於[[Strict]]
的值
[[Strict]]
: 類型爲boolean。若是爲true表明這是一個嚴格模式下的函數
6. Let formals
等於[[FormalParameters]]
的值
[[FormalParameters]]
:類型爲Node。指向函數的形參列表。
7. Let parameterNames
等於formals
的BoundNames
,即若是形參爲x, y那麼parameterNames
爲['x', 'y']
8. 若是parameterNames
裏有重複的,將hasDuplicates
置爲true
,不然置爲false
9. Let simpleParameterList
等於formals
的IsSimpleParameterList
IsSimpleParameterList
:若是形參爲空或者只是普通的標識符則返回true,其餘的如形參爲rest參數(...x),普通參數加rest參數(x, ...y),參數有默認值,參數有解構賦值等等,都返回false
10. Let hasParameterExpressions
等於formals
的ContainsExpression
的值
ContainsExpression
:形參含有默認值則爲true,不然爲false
11. Let varNames
等於函數的VarDeclaredNames
(只包含函數體裏的變量,不包含形參)的值
12. Let varDeclarations
等於函數的VarScopedDeclarations
的值
VarDeclaredNames
與VarScopedDeclarations
的區別:VarDeclaredNames
是一個類型爲Name
的Set
(Name
只包含標識符名,做用域等等)。而VarScopedDeclarations
是一個類型爲StatementListItem
的List
(StatementListItem
表明的是語句元素,ES一共有14種語句),就這裏的語句而言,指的是VariableStatement
,對於咱們解析而已,是把語句(也就是Statement)看成一個語法樹節點
13. Let lexicalNames
等於函數的LexicallyDeclaredNames
(不包含var和function聲明)
14. Let functionNames
等於一個空的List
15. Let functionsToInitialize
等於一個空的List
16. 對於變量varDeclarations
其中的每一個元素d
,若是d
既不是VariableDeclaration
也不是ForBinding
(for in或者for of結構裏面進行聲明)。那麼:
進行Assert
(斷言),判斷d
是不是函數聲明或者構造器聲明
Let fn
等於d
的BoundNames
若是fn
不是functionNames
裏的元素,那麼
將fn
用頭插法插入functionNames
注意若是fn
有屢次重複出現,則以最後一次爲準
將d
用頭插法插入functionsToInitialize
17. 聲明一個argumentsObjectNeeded
,賦值爲true
18. 若是func
的內嵌屬性[[ThisMode]]
的值爲lexical
,那麼
將argumentsObjectNeeded
賦值爲false
(注意箭頭函數沒有arguments
對象)
[[ThisMode]]
:做用是定義在函數形參和函數體內如何解析this
引用。值爲lexical
表明this
指向詞法閉包的this
值(詞法閉包就是咱們常說的閉包,具體能夠看個人上一篇文章),strict
表明this
值徹底由函數調用提供。global
表明this
值爲undefined
19. 不然(接上)若是arguments
是parameterNames
(在第7步聲明)的一個元素(也就是形參裏面咱們使用了arguments
做爲標識符), 那麼將argumentsObjectNeeded
賦值爲false
20. 不然(接上)若是hasParameterExpressions
(在第10步聲明)等於false
,那麼
若是arguments
是functionNames
(在第14步聲明)的一個元素,或者是lexicalNames
(在第13步聲明)的一個元素,那麼將argumentsObjectNeeded
賦值爲false
21. 對於parameterNames
(在第7步聲明)中每一個元素paramName
:
a.Let alreadyDeclared
等於envRec.HasBinding(paramName)
的值(即判斷當前環境中是否綁定過paramName
)
b.注意:早期的錯誤檢查確保了多個重複的形參參數數名只可能出如今形參沒有默認值和rest參數的非嚴格模式下的函數中:
1. function func(x, x = 2) {} // 報錯 2. function func(x, ...x) {} // 報錯 3. function func(x, x) {} // 不報錯 4. 'use strict'; function func(x, x) {} // 報錯
c.若是alreadyDeclared
等於false
,那麼:
c.1 Let status
等於envRec.CreateMutableBinding(paramName)
(表1中有這個方法)的值(即將聲明的參數綁定到函數的做用域中)
c.2 若是hasDuplicates
(在第8步聲明)等於true
,那麼:
Let status
等於envRec.InitializeBinding(paramName, undefined)
(表1中有這個方法)的值
c.3 斷言:在上面兩步操做中(c.1和c.2),status
不多是一個 abrupt completion
(能夠簡單的理解爲break,continue,return和throw操做)
22. 若是argumentsObjectNeeded
(第17-20步改變)等於true
,那麼:
a.若是strict
(第5步聲明)等於true
或者simpleParameterList
(第9步聲明)等於false
,那麼:
a.1 Let ao
等於CreateUnmappedArgumentsObject(argumentsList)
的值
b.不然(接上面的a步驟):
b.1 注意:mapped argument
(與上面的Unmapped對應)對象僅在非嚴格模式下且形參沒有rest
參數,默認值,解構賦值的函數中提供。(知足這三個條件其實simpleParameterList
就爲true
了)
b.2 Let ao
等於CreateMappedArgumentsObject(func, formals, argumentsList, env)
的值
注:CreateUnmappedArgumentsObject和
CreateMappedArgumentsObject簡單來講就是根據參數形式的不一樣建立不一樣的
arguments`對象
c.ReturnIfAbrupt(ao)
d.若是strict
等於true
,那麼:
d.1 Let status
等於envRec.CreateImmutableBinding("arguments")
(表1中有介紹)的值
e.不然(接上面的c步驟),Let status
等於envRec.CreateMutableBinding("arguments")
(表1中有介紹)的值
f.斷言:status
不多是一個 abrupt completion
g.執行envRec.InitializeBinding("arguments", ao)
(表1中有介紹)
h.向parameterNames
(第7步中聲明)中appendarguments
23. Let iteratorRecord
等於Record {[[iterator]]: CreateListIterator(argumentsList), [[done]]: false}
(即創建一個內置迭代器屬性,讓arguments
變成可迭代的)
24. 若是hasDuplicates
(第8步中聲明)等於true
,那麼:
a.Let formalStatus
等於formals
去調用IteratorBindingInitialization
,用iteratorRecord
和undefined
做爲參數的返回值
25. 不然(接上面的24步驟):
a.Let formalStatus
等於formals
去調用IteratorBindingInitialization
,用iteratorRecord
和env
做爲參數的返回值(能夠看到只有最後一個參數和24步不同)
IteratorBindingInitialization(iteratorRecord,environment)
:當environment
爲undefined
的時候,這意味着應該用一個PutValue
(即將一個值放入一個對象)操做去初始化值。這是針對非嚴格模式狀況下的一個考慮(由於嚴格模式下在24步應該是false
)。在這種狀況下,形參被預初始化,目的是解決多個參數名相同的問題。
26. ReturnIfAbrupt(formalStatus)
27. 若是hasParameterExpressions
(第10步聲明)等於false
,那麼:
a.注意:對於形參和聲明提取的變量,僅僅只須要一個單一的詞法環境
b.Let instantiatedVarNames
等於parameterNames
的一個副本
c.對於varNames
(第11步中聲明)的每一個元素n
:
c.1 若是n
不是instantiatedVarNames
裏的元素,那麼:
c.1.1 appendn
到instantiatedVarNames
中
c.1.2 Let status
等於envRec.CreateMutableBinding(n)
c.1.3 斷言:status
不多是一個 abrupt completion
c.1.4 執行envRec.InitializeBinding(n, undefined)
d.Let varEnv
等於env
e.Let varEnvRec
等於envRec
28. 不然(接上面的27步驟):
a.注意:一個單獨的ER是有必要的,目的是確保形參中的表達式建立的閉包對函數體的變量不具備可訪問性(即咱們提到的"第三做用域")
b.Let varEnv
等於NewDeclarativeEnvironment(env)
的值(即建立一個新的詞法環境,它的ER裏沒有任何綁定,這個ER的外部或者說父級詞法環境在這裏就是env)
c.Let varEnvRec
等於varEnv
的ER
d.將calleeContext
(第1步中聲明)的VariableEnvironment
設爲varEnv
e.Let instantiatedVarNames
等於一個空的List
f.對於varNames
中的每一個元素n
:
f.a 若是n
不是instantiatedVarNames
中的元素,那麼:
f.a.1 appendn
到instantiatedVarNames
中
f.a.2 Let status
等於varEnvRec.CreateMutableBinding(n)
(varEnvRec
在27.e步或者28.c步中聲明,CreateMutableBinding
參考表1)的值
f.a.3 斷言:status
不多是一個 abrupt completion
f.a.4 若是n
不是parameterNames
(第7步中聲明)中的元素,或者n
是functionNames
(第14步中聲明)中的元素,Let initialValue
等於undefined
f.a.5 不然(接上面的f.a.4步驟):
f.a.5.1 Let initialValue
等於envRec.GetBindingValue(n, false)
(envRec
在第3步中聲明,GetBindingValue
參考表1)
f.a.5.2 ReturnIfAbrupt(initialValue)
f.a.6 執行varEnvRec.InitializeBinding(n, initialValue)
(varEnvRec
在27.e步或者28.c步中聲明,InitializeBinding
參考表1)
f.a.7 注意:形參中相同標識符的變量,當它們對應的形參初始化的時候,它們的值是同樣的。(意思就是好比function func(x, x) {}
,調用時func(111)
,那麼當第二個x初始化的時候,第一個x也就變成undefined了,由於它們的值要保持一致,因此最後x爲undefined)
29. 注意:附錄B.3.3
在這一點有額外的步驟(有興趣能夠去看看,主要是介紹了瀏覽器宿主環境對於塊級函數聲明的解析和規範的差別)
30. 若是strict
等於false
,那麼:
a.Let lexEnv
等於NewDeclarativeEnvironment(varEnv)
的值(即建立一個新的詞法環境,它的ER裏沒有任何綁定,這個ER的外部或者說父級詞法環境在這裏就是varEnv)
b.注意:非嚴格模式下的函數對於頂層聲明採用的是一個單獨的詞法做用域,所以直接調用eval
(var a = eval; a(xx)
這叫間接調用)可以對那些已經聲明過的會致使衝突。在嚴格模式下這是不須要的,由於嚴格模式下的eval
老是把聲明放到一個新的ER中
function qq(){var a = 1; eval('var a = 55;'); console.log(a);} // 輸出55 "use strict"; function qq(){var a = 1; eval('var a = 55;'); console.log(a);} // 輸出1
31.不然(接上面的30步驟),Let lexEnv
等於varEnv
(在27.d或者28.b中聲明)
32. Let lexEnvRec
等於lexEnv
的ER
33. 將calleeContext
(第1步中聲明)的ER設置爲lexEnv
34. Let lexDeclarations
等於函數的LexicallyScopedDeclarations
35. 對於lexDeclarations
中的每一個元素d
:
a.注意:一個詞法聲明的標識符不能和函數,產生器函數,形參或者其餘變量名相同。詞法聲明的標識符只會在這裏實例化而不是初始化。
b.對於BoundNames
中的每一個元素dn
:
b.1 若是d是常量聲明,那麼:
b.1.1 Let status
等於lexEnvRec.CreateImmutableBinding(dn, true)
b.1.2 Let status
等於lexEnvRec.CreateMutableBinding(dn, false)
c.斷言:status
不多是一個 abrupt completion
36. 對於functionsToInitialize
中的每一個解析過的語法短語(這裏的短語指的是編譯原理裏的短語)f
:
a.Let fn
做爲f
的BoundNames
的惟一元素
b.Let fo
等於執行InstantiateFunctionObject(f, lexEnv)
的結果
InstantiateFunctionObject(f, lexEnv)
:
c.Let status
等於varEnvRec.SetMutableBinding(fn, fo, false)
d.斷言:status
不多是一個 abrupt completion
37. 返回NormalCompletion(empty)
(即返回 Completion{[[type]]: normal, [[value]]: empty, [[target]]:empty}
)
注意:附錄B.3.3關於上面的算法提供了一種擴展,這種擴展對於瀏覽器在ES2015以前實現ECMAScript向後兼容是有必要的。(也就是咱們常說的ployfill)
注意:形參的Initializers
(即默認值)也許包含eval
表達式。任何在這個eval
裏面聲明的變量只能在這個eval
內才能訪問。
在探索和翻譯的過程當中,確實是遇到了一些困難,包括到如今也還有一些困惑仍未解決。通過此次探索,想到一位大牛曾回答過"做爲程序員,哪些網站是必須瞭解的"的問題,他的回答是"除了github和stackoverflow,應該沒有其餘是必須的",算是比較深入的體會到了一這點,不少東西google和wiki都是找不到的,只能求助於so,沒有的話還須要本身提問和gh上提issue。
一條評論可能又會提到其餘地方,其餘地方又會連接到不一樣的人,不一樣的技術,不一樣的想法。這樣都去瀏覽或者瞭解一番,便能開闊眼界,從一個單一知識點入手,不僅僅是解決這一個問題。或許咱們還能學到不少新的知識,方式,想法,瞭解一些新的工具,認識一些有趣的人。