JavaScript進階(四):爲什麼要報錯,報錯的作用,其他的問題和收穫

上一篇博客中,我們用類實現了一個簡單的列表,但是卻有很多很多的問題,這裏,我們就一起來看看。

首先,第一個問題,我們現在是直接就把 title 作爲自己的一個屬性暴露給外面了:

注意!這樣是非常不好的,爲什麼不好呢?

面向對象其實防的不是外人,防的是自己人。什麼意思呢?

比方說,現在就有人取巧,直接設置這個 title,不走你的 render,那這時候有效果嗎?

可以看到,是沒效果的。

並且,這個時候最邪門的就是,頁面上的標題是熱門主播,沒錯。

但是我 list 裏面的 title 其實是已經更新了:

你會發現這個狀態跟實際的 UI 就對不上了,這時候狀態就混亂了。

所以面向對象其實是一個保護的作用,我們不希望它直接暴露在外面。

而且我們只是改了一個普普通通的 title,如果我改個別的,問題其實更大。

所以,這是第一個問題。

 

第二個問題就是,我們現在還得手動的去渲染:

這個其實不好,在我的理解當中,應該是我給你 new 出來了,你就能完成自己的工作。

所以這個倒是好辦,我們可以直接在初始化的時候,直接 render 一次,就完事了。

這樣的話,我們用起來就方便很多了。

 

然後第三個問題,是關於錯誤的。

這個本質上其實跟我們寫類無關,而是在你寫所有的代碼,所有程序的時候,都需要有的一個思路。

什麼意思呢?

首先,我先來明確的問大家一個問題,請問你寫代碼的時候有沒有出過錯?

說我這輩子寫代碼從來一個錯都沒出過,這種人基本就是江湖騙子。

所以說,寫代碼有錯這事,太正常了。

有錯是常態,那麼反過來,我們要做的就是能夠儘早的找到錯誤,這個是至關重要的。

既然我們做不到不犯錯,那麼我們就要想辦法儘早的找到錯誤。

 

那麼接下來的這個問題就是:

現在這個 data,我們在 render 裏面要用它來做 forEach 循環的。

那你能保證用戶給你傳的這個數據一定對嗎?

比如,用這個類的人,他給你傳了個其他的東西:123

注意,這個時候報錯是肯定的,但它報的是第 44 行。

這裏問下告訴大家,這個報的第 44 行,請問對調試這個錯誤有沒有幫助?

不能說完全沒有,但是微乎其微。

爲什麼呀?

因爲這個錯,並不是在第 44 行出來的。

在哪出來的?

其實是在第 69 行出來的。

需要注意的是,我們現在這個代碼很少,就 60 多行,邏輯也很簡單。

但如果你是一個非常大型,非常龐大的庫,動不動就幾千多行,那直接給你報一個第 3782 行有問題,你根本就不知道是怎麼回事。

這個其實就回到了我們上面說的那個問題,有錯正常,我們都是人,是人就會犯錯,那怎麼辦?

你要儘早的去找到這個錯誤。說白了,一個直接的結論,你需要校驗你的參數。

 

比如,我現在就做一個最簡單的校驗:

首先,這個 parent 能可選嗎?

不能,你必須得給,你不給,我就給你報錯。

然後,我在做其他參數的一些判斷,比方說 title,我要求它必須是個字符串,如果不是,我就報錯。

以及最重要的 data,它必須得是一個 Array,如果不是,我也報錯。

這裏我用 data instanceof Array 來判斷它是不是一個數組。

那有人可能會說,data 要是 Array 的子類呢?

Array 的子類,是不是也得有 forEach,也得有 length 和下標,所以我一樣能用。

 

那麼注意,這個時候你可以看到,首先,同樣會報錯:

那有人說,你這啥都沒解決啊?

首先,你注意一個事,現在這個報錯信息和剛纔相比如何?是不是一眼望過去,就知道怎麼一回事了?

然後,你還可以明確的看到,現在報的這個錯,它是在 index.html 第21行。

但是在它下面,會明確的告訴你,它真正這個錯是從 81 行出來的

所以,這個是非常非常非常重要的,這裏再重複一遍:

寫代碼出錯,沒關係,誰都一樣,你要做的是儘早找到錯誤。

說白了,就是你在參數剛進去的時候,就把它攔住,然後檢查下它有沒有錯。

有錯趕緊報,不要讓程序再往下執行了。

否則再往下執行,第一,容易把一些狀態弄亂了,如果下面有一些跟後臺數據庫去交互的一些東西,就可能把數據直接毀了,這個就更麻煩。

第二,這個錯,你越早報出來,越容易定位,你越晚報出來,越定位不了。

 

但是又有一個問題,我老得 if if 的寫,這得寫多少個啊?

這裏,我個人有個習慣,當然我覺得這個習慣挺好,所以推薦給大家。

我個人更習慣把這個報錯的東西直接封裝起來,我喜歡叫它 assert,斷言。

所謂斷言,其實就是斷定的意思。斷定,就是說我擔保這事一定是這樣。

當然斷言的庫有很多,你可以直接引一個,也可以自己封。

這裏,我一般給它 2 個參數。

第一個參數就是說,我給你一個表達式,我要判斷這個表達式是真還是假。

第二個參數,我給你一個 msg,報錯信息。

意思就是,如果表達式爲假,那麼我就 throw 一個 Error 出來,並且報錯的內容,就是這個 msg。

然後我們引入 assert.js,這個時候我這寫起來就非常的明確了。

比如:

我斷言,一定有 parent,沒有我就給你報錯。

我斷言,title 一定是個 string,不是我就給你報錯。

我斷言,data 一定是個數組,不是我就給你報錯。

可以看到,這樣寫是不是就更明確,讓人閱讀起來也方便,並且更容易理解了。

在所有的函數裏面,我一般習慣於,一定是開頭先把參數校驗個明白。寧肯費這個功夫,也省得後面慢慢去找錯。

然後我們來看看,它還能不能把錯報出來:

可以看到,是一樣報錯的,但這樣寫起來,其實更加的方便。

 

然後接下來這個問題,其實是一個改進,就是我這個 parent,現在用起來不太方便。

在前面我們有說過,最好是方便去使用這個類的人。

那你說有沒有可能,用的時候我自己不去選,就傳個字符串進去,讓你幫我選:

那這個時候,我們就要考慮一個問題,就是這個 parent,要兼顧到多種的可能性。

它既有可能是個字符串,讓我去幫你選。也有可能本身就直接是個 html 元素,那我就不用管了,直接用就行。

所以說,我們如何能夠讓 parent 自動的去判斷,適應更多的範圍,這又是一個問題。

 

以及我們還有一個問題,這個問題更嚴重,就是 render:

注意,你現在每一次 render,都是要重新生成一遍所有的元素。

我堅信大家肯定跟我一樣,覺得這事不靠譜,爲什麼呀?性能太差了,兄弟。

現在這個列表還小,才3個,沒事。如果說這個列表用的特別多,裏面還有圖,亂七八糟的一大堆等等,那這個性能是要死人的,絕對會出大事。

所以,每一次都完全的去重新生成元素,這是一個很嚴重很嚴重的問題。

 

當然有人說,其實也不用。我可以把它的變量都挑出來,比如 title 變了,我就只改變它。當然沒問題,但是你這個代碼是不是就會變的非常混亂。

所以最簡單的辦法:如何能夠既偷懶,又不用去寫的那麼複雜,然後我這邊性能還很高,只更新有必要的那個元素?

我相信大家心裏都有這個結果的,沒錯,虛擬 DOM。

虛擬 DOM 就是幹這個用的,它也是爲了這個而存在的。

 

所以,你會發現,我們這東西大嗎?真不大。但是裏面的問題少嗎?太多了。

雖然我們把這個類實現了,也找到了一些問題,但是完了嗎?還沒完。

 

接下來,我們來具體看看上面提到的問題,我們先恢復到之前正確的狀態,要不然代碼就都是錯的了。現在這時候,別的我們先不管,至少恢復正常可以去用了,那麼繼續:

比方說,我現在想去 set 一個 title。

你說我不能直接改 title,這個我認了。

但我現在用 setTitle 設置完之後,好使嗎?

並不好使。

但事實上 title 更沒更新?

可以看到 title 實際上已經更新了。

缺一個啥?

是不是缺一個 render,你要重新去渲染才行。

但這時候有問題了,你說我 setTitle,set 完之後,再還得手動的去重新 render 一下:

它能變,但這是不是很麻煩呀?

所以最好是在它裏面,set 完之後,立刻就 render 一下,包括 setData 也是:

但是這樣做,至少又帶來了兩個問題:

1,我們說了要方便用這個類的人。這事沒錯,一點不假。

那麼這時候你有沒有考慮過,你在類裏面搞了那麼多的方法出來,人家用着一點都不方便。

其實最方便的是什麼?

是我可以直接賦值:list.title = 'xxx'

並且,賦值後,它這邊就有變化。這是最方便的,但現在做不到。

那麼,我如何能夠對一個屬性賦了值之後,它這邊自動的響應我這個賦值之後的變化呢?

這詞都已經出來了,響應式,我們在後面的博客中會做這個事。

 

2,你還可以看到,我們代碼裏面的 render 用的可一點都不少。

在我每一次有數據變化的時候,這個 render 都要重新執行。

所以這個 render 的效率,就直接決定了我整個程序效率的高低。

那這個 render 要不要好好的優化?

絕對是要的,怎麼優化?上面也說了,虛擬DOM,這些問題,我們都留到後面。

我們現在僅僅只是來想辦法實現這樣一個基礎的東西,後面會慢慢改進。

 

那麼在最後,我們總結下這篇博客到底說了些什麼:

首先,就是我們人類犯錯誤很正常,但是你要如何儘早的去定位這些問題,很簡單,就是越多的檢查越好。

當然有人可能會說,你這寫了那麼多的 assert,判斷也是代碼,它也是需要一些資源和開銷的,那麼這個多了會不會慢?

首先肯定一點,會。

其次,你這個東西其實在真正的生產環境裏面是不需要的。

大家應該知道有很多很多的工具,可以在你打包這個程序最終生成線上版本的時候,是可以按照你的需求去掉一些東西的。

比方說 alert,console.log,assert,這個都是可以自定義的。

所以,assert 你就放心大膽的往死里加,一點事沒有。等將來真正打包線上版本的時候,它都是能去掉的。

所以,我們要儘早的去定位,assert 越多越好。

 

那麼這個 assert,也是我個人平常在用的方法,就是直接去封裝一個斷言方法,兩行代碼的事。

直接去做一個 assert 斷言,然後在這裏面,你就可以直接去 throw 一個 Error 出來。

throw new Error 有兩個作用:

第一個作用是,防止這個代碼繼續往下執行,來造成更大的破壞。

第二個作用,它既然報錯了,我們就可以看到它是哪行哪句哪個東西報的,這樣我們調試起來也是及其的方便。

 

當然,我們在實現這個類的過程當中,其實也有很多的收穫,比方說:

1,我們有一個原則,就是我們這些屬性,儘量的不要直接去暴露出來。這樣是非常不好的。

2,我們在渲染的時候,切記不要每次都去重新渲染,這樣開銷是非常大的。

當然這兩個話題,我們都會在後面的博客中進一步的深入。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章