這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

一、面向物件
一般使用字面量的形式直接創建物件,但是這種創建方式對于創建大量相似物件的時候,會產生大量的重復代碼,但 js和一般的面向物件的語言不同,在 ES6 之前它沒有類的概念,但是可以使用函式來進行模擬,從而產生出可復用的物件創建方式,常見的有以下幾種:
(1)第一種是工廠模式,工廠模式的主要作業原理是用函式來封裝創建物件的細節,從而通過呼叫函式來達到復用的目的,但是它有一個很大的問題就是創建出來的物件無法和某個型別聯系起來,它只是簡單的封裝了復用代碼,而沒有建立起物件和型別間的關系,
(2)第二種是建構式模式,js 中每一個函式都可以作為建構式,只要一個函式是通過 new 來呼叫的,那么就可以把它稱為建構式,執行建構式首先會創建一個物件,然后將物件的原型指向建構式的 prototype 屬性,然后將執行背景關系中的 this 指向這個物件,最后再執行整個函式,如果回傳值不是物件,則回傳新建的物件,因為 this 的值指向了新建的物件,因此可以使用 this 給物件賦值,建構式模式相對于工廠模式的優點是,所創建的物件和建構式建立起了聯系,因此可以通過原型來識別物件的型別,但是建構式存在一個缺點就是,造成了不必要的函式物件的創建,因為在 js 中函式也是一個物件,因此如果物件屬性中如果包含函式的話,那么每次都會新建一個函式物件,浪費了不必要的記憶體空間,因為函式是所有的實體都可以通用的,
(3)第三種模式是原型模式,因為每一個函式都有一個 prototype 屬性,這個屬性是一個物件,它包含了通過建構式創建的所有實體都能共享的屬性和方法,因此可以使用原型物件來添加公用屬性和方法,從而實作代碼的復用,這種方式相對于建構式模式來說,解決了函式物件的復用問題,但是這種模式也存在一些問題,一個是沒有辦法通過傳入引數來初始化值,另一個是如果存在一個參考型別如 Array 這樣的值,那么所有的實體將共享一個物件,一個實體對參考型別值的改變會影響所有的實體,
(4)第四種模式是組合使用建構式模式和原型模式,這是創建自定義型別的最常見方式,因為建構式模式和原型模式分開使用都存在一些問題,因此可以組合使用這兩種模式,通過建構式來初始化物件的屬性,通過原型物件來實作函式方法的復用,這種方法很好的解決了兩種模式單獨使用時的缺點,但是有一點不足的就是,因為使用了兩種不同的模式,所以對于代碼的封裝性不夠好,
(5)第五種模式是動態原型模式,這一種模式將原型方法賦值的創建程序移動到了建構式的內部,通過對屬性是否存在的判斷,可以實作僅在第一次呼叫函式時對原型物件賦值一次的效果,這一種方式很好地對上面的混合模式進行了封裝,
(6)第六種模式是寄生建構式模式,這一種模式和工廠模式的實作基本相同,我對這個模式的理解是,它主要是基于一個已有的型別,在實體化時對實體化的物件進行擴展,這樣既不用修改原來的建構式,也達到了擴展物件的目的,它的一個缺點和工廠模式一樣,無法實作物件的識別,
2. 物件繼承的方式有哪些?
(1)第一種是以原型鏈的方式來實作繼承,但是這種實作方式存在的缺點是,在包含有參考型別的資料時,會被所有的實體物件所共享,容易造成修改的混亂,還有就是在創建子型別的時候不能向超型別傳遞引數,
(2)第二種方式是使用借用建構式的方式,這種方式是通過在子型別的函式中呼叫超型別的建構式來實作的,這一種方法解決了不能向超型別傳遞引數的缺點,但是它存在的一個問題就是無法實作函式方法的復用,并且超型別原型定義的方法子型別也沒有辦法訪問到,
(3)第三種方式是組合繼承,組合繼承是將原型鏈和借用構造函陣列合起來使用的一種方式,通過借用建構式的方式來實作型別的屬性的繼承,通過將子型別的原型設定為超型別的實體來實作方法的繼承,這種方式解決了上面的兩種模式單獨使用時的問題,但是由于我們是以超型別的實體來作為子型別的原型,所以呼叫了兩次超類的建構式,造成了子型別的原型中多了很多不必要的屬性,
(4)第四種方式是原型式繼承,原型式繼承的主要思路就是基于已有的物件來創建新的物件,實作的原理是,向函式中傳入一個物件,然后回傳一個以這個物件為原型的物件,這種繼承的思路主要不是為了實作創造一種新的型別,只是對某個物件實作一種簡單繼承,ES5 中定義的 Object.create() 方法就是原型式繼承的實作,缺點與原型鏈方式相同,
(5)第五種方式是寄生式繼承,寄生式繼承的思路是創建一個用于封裝繼承程序的函式,通過傳入一個物件,然后復制一個物件的副本,然后物件進行擴展,最后回傳這個物件,這個擴展的程序就可以理解是一種繼承,這種繼承的優點就是對一個簡單物件實作繼承,如果這個物件不是自定義型別時,缺點是沒有辦法實作函式的復用,
(6)第六種方式是寄生式組合繼承,組合繼承的缺點就是使用超型別的實體做為子型別的原型,導致添加了不必要的原型屬性,寄生式組合繼承的方式是使用超型別的原型的副本來作為子型別的原型,這樣就避免了創建不必要的屬性,
二、垃圾回收與記憶體泄漏
1. 瀏覽器的垃圾回識訓制
(1)垃圾回收的概念
垃圾回收:JavaScript代碼運行時,需要分配記憶體空間來儲存變數和值,當變數不在參與運行時,就需要系統識訓被占用的記憶體空間,這就是垃圾回收,
回識訓制:
- Javascript 具有自動垃圾回識訓制,會定期對那些不再使用的變數、物件所占用的記憶體進行釋放,原理就是找到不再使用的變數,然后釋放掉其占用的記憶體,
- JavaScript中存在兩種變數:區域變數和全域變數,全域變數的生命周期會持續要頁面卸載;而區域變數宣告在函式中,它的生命周期從函式執行開始,直到函式執行結束,在這個程序中,區域變數會在堆或堆疊中存盤它們的值,當函式執行結束后,這些區域變數不再被使用,它們所占有的空間就會被釋放,
- 不過,當區域變數被外部函式使用時,其中一種情況就是閉包,在函式執行結束后,函式外部的變數依然指向函式內部的區域變數,此時區域變數依然在被使用,所以不會回收,
(2)垃圾回收的方式
瀏覽器通常使用的垃圾回收方法有兩種:標記清除,參考計數,
1)標記清除
- 標記清除是瀏覽器常見的垃圾回收方式,當變數進入執行環境時,就標記這個變數“進入環境”,被標記為“進入環境”的變數是不能被回收的,因為他們正在被使用,當變數離開環境時,就會被標記為“離開環境”,被標記為“離開環境”的變數會被記憶體釋放,
- 垃圾收集器在運行的時候會給存盤在記憶體中的所有變數都加上標記,然后,它會去掉環境中的變數以及被環境中的變數參考的標記,而在此之后再被加上標記的變數將被視為準備洗掉的變數,原因是環境中的變數已經無法訪問到這些變數了,最后,垃圾收集器完成記憶體清除作業,銷毀那些帶標記的值,并回收他們所占用的記憶體空間,
2)參考計數
- 另外一種垃圾回識訓制就是參考計數,這個用的相對較少,參考計數就是跟蹤記錄每個值被參考的次數,當宣告了一個變數并將一個參考型別賦值給該變數時,則這個值的參考次數就是1,相反,如果包含對這個值參考的變數又取得了另外一個值,則這個值的參考次數就減1,當這個參考次數變為0時,說明這個變數已經沒有價值,因此,在在機回收期下次再運行時,這個變數所占有的記憶體空間就會被釋放出來,
- 這種方法會引起回圈參考的問題:例如:
obj1和obj2通過屬性進行相互參考,兩個物件的參考次數都是2,當使用回圈計數時,由于函式執行完后,兩個物件都離開作用域,函式執行結束,obj1和obj2還將會繼續存在,因此它們的參考次數永遠不會是0,就會引起回圈參考,
function fun() {
let obj1 = {};
let obj2 = {};
obj1.a = obj2; // obj1 參考 obj2
obj2.a = obj1; // obj2 參考 obj1
}
這種情況下,就要手動釋放變數占用的記憶體:
obj1.a = null obj2.a = null
3)減少垃圾回收
雖然瀏覽器可以進行垃圾自動回收,但是當代碼比較復雜時,垃圾回收所帶來的代價比較大,所以應該盡量減少垃圾回收,
●對陣列進行優化:在清空一個陣列時,最簡單的方法就是給其賦值為[ ],但是與此同時會創建一個新的空物件,可以將陣列的長度設定為0,以此來達到清空陣列的目的,
●對object進行優化:物件盡量復用,對于不再使用的物件,就將其設定為null,盡快被回收,
●對函式進行優化:在回圈中的函式運算式,如果可以復用,盡量放在函式的外面,
2. 哪些情況會導致記憶體泄漏
以下四種情況會造成記憶體的泄漏:
●意外的全域變數:由于使用未宣告的變數,而意外的創建了一個全域變數,而使這個變數一直留在記憶體中無法被回收,
●被遺忘的計時器或回呼函式:設定了 setInterval 定時器,而忘記取消它,如果回圈函式有對外部變數的參考的話,那么這個變數會被一直留在記憶體中,而無法被回收,
●脫離 DOM 的參考:獲取一個 DOM 元素的參考,而后面這個元素被洗掉,由于一直保留了對這個元素的參考,所以它也無法被回收,
●閉包:不合理的使用閉包,從而導致某些變數一直被留在記憶體當中,
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/472297.html
標籤:JavaScript
下一篇:組件設計要求
