整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          前端開發必備 - JavaScript 之 內存管理

          前端開發必備 - JavaScript 之 內存管理

          .內存管理

          前置知識

          絕大多數的程序語言,他們的內存生命周期基本一致:

          1. 分配所需使用的內存 ——(分配內存)
          2. 使用分配到的內存(讀、寫) ——(使用內存)
          3. 不需要時將其釋放\歸還 ——(釋放內存)

          對于所有的編程語言,第二部分都是明確的。而第一和第三部分在底層語言中是明確的。

          但在像JavaScript這些高級語言中,大部分都是隱含的,因為JavaScript具有自動垃圾回收機制(Garbage collected)。

          因此在做JavaScript開發時,不需要關心內存的使用問題,所需內存分配和無用內存回收,都完全實現自動管理。

          1.概述

          像C語言這樣的高級語言一般都有底層的內存管理接口,比如 malloc()和free()。另一方面,JavaScript創建變量(對象,字符串等)時分配內存,并且在不再使用它們時“自動”釋放。 后一個過程稱為垃圾回收。這個“自動”是混亂的根源,并讓JavaScript(和其他高級語言)開發者感覺他們可以不關心內存管理。 這是錯誤的。 ——《MDN JavaScript 內存管理》

          MDN中的介紹告訴我們,作為JavaScript開發者,還是需要去了解內存管理,雖然JavaScript已經給我們做好自動管理。

          2.JavaScript內存生命周期

          2.1 分配內存

          在做JavaScript開發時,我們定義變量的時候,JavaScript便為我們完成了內存分配:

          var num=100; // 為數值變量分配內存
          var str='pingan'; // 為字符串變量分配內存
          var obj={
           name : 'pingan'
          }; // 為對象變量及其包含的值分配內存
          var arr=[1, null, 'hi']; // 為數組變量及其包含的值分配內存
          function fun(num){
           return num + 2;
          }; // 為函數(可調用的對象)分配內存
          // 函數表達式也能分配一個對象
          someElement.addEventListener('click', function(){
           someElement.style.backgroundColor='blue';
          }, false);
          

          另外,通過調用函數,也會分配內存:

          // 類型1. 分配對象內存
          var date=new Date(); // 分配一個Date對象
          var elem=document.createElement('div'); // 分配一個DOM元素
          // 類型2. 分配新變量或者新對象
          var str1="pingan";
          var str2=str1.substr(0, 3); // str2 是一個新的字符串
          var arr1=["hi", "pingan"];
          var arr2=["hi", "leo"];
          var arr3=arr1.concat(arr2); // arr3 是一個新的數組(arr1和arr2連接的結果)
          

          2.2 使用內存

          使用內存的過程實際上是對分配的內存進行讀取與寫入的操作。

          通常表現就是使用定義的值。

          讀取與寫入可能是寫入一個變量或者一個對象的屬性值,甚至傳遞函數的參數。

          var num=1;
          num ++; // 使用已經定義的變量,做遞增操作
          

          2.3 釋放內存

          當我們前面定義好的變量或函數(分配的內存)已經不需要使用的時候,便需要釋放掉這些內存。這也是內存管理中最難的任務,因為我們不知道什么時候這些內存不使用。

          很好的是,在高級語言解釋器中,已經嵌入“垃圾回收器”,用來跟蹤內存的分配和使用,以便在內存不使用時自動釋放(這并不是百分百跟蹤到,只是個近似過程)。

          3.垃圾回收機制

          就像前面提到的,“垃圾回收器”只能解決一般情況,接下來我們需要了解主要的垃圾回收算法和它們局限性。

          3.1 引用

          垃圾回收算法主要依賴于引用的概念。

          即在內存管理環境中,一個對象如果有權限訪問另一個對象,不論顯式還是隱式,稱為一個對象引用另一個對象。

          例如:一個JS對象具有對它原型的引用(隱式引用)和對它屬性的引用(顯式引用)。 注意:

          這里的對象,不僅包含JS對象,也包含函數作用域(或全局詞法作用域)。

          3.2 引用計數垃圾收集

          這個算法,把“對象是否不再需要”定義為:當一個對象沒有被其他對象所引用的時候,回收該對象。這是最初級的垃圾收集算法。

          var obj={
           leo : {
           age : 18
           };
          };
          

          這里創建2個對象,一個作為leo的屬性被引用,另一個被分配給變量obj。

          // 省略上面的代碼
          /*
          我們將前面的
           {
           leo : {
           age : 18
           };
           };
          稱為“這個對象”
          */
          var obj2=obj; // obj2變量是第二個對“這個對象”的引用
          obj='pingan'; // 將“這個對象”的原始是引用obj換成obj2
          var leo2=obj2.leo; // 引用“這個對象”的leo屬性
          

          可以看出,現在的“這個對象”已經有2個引用,一個是obj2,另一個是leo2。

          obj2='hi'; 
          // 將obj2變成零引用,因此,obj2可以被垃圾回收
          // 但是它的屬性leo還在被leo2對象引用,所以還不能回收
          leo2=null;
          // 將leo變成零引用,這樣obj2和leo2都可以被垃圾回收
          

          這個算法有個限制:

          無法處理循環引用。即兩個對象創建時相互引用形成一個循環。

          function fun(){
           var obj1={}, obj2={};
           obj1.leo=obj2; // obj1引用obj2
           obj2.leo=obj1; // obj2引用obj1
           return 'hi pingan';
          }
          fun();
          

          可以看出,它們被調用之后,會離開函數作用域,已經沒有用了可以被回收,然而引用計數算法考慮到它們之間相互至少引用一次,所以它們不會被回收。

          實際案例:

          在IE6,7中,使用引用計數方式對DOM對象進行垃圾回收,常常造成對象被循環引用導致內存泄露:

          var obj;
          window.onload=function(){
           obj=document.getElementById('myId');
           obj.leo=obj;
           obj.data=new Array(100000).join('');
          };
          

          可以看出,DOM元素obj中的leo屬性引用了自己obj,造成循環引用,若該屬性(leo)沒有移除或設置為null,垃圾回收器總是且至少有一個引用,并一直占用內存,即使從DOM樹刪除,如果這個DOM元素含大量數據(如data屬性)則會導致占用內存永遠無法釋放,出現內存泄露。

          3.3 標記清除算法

          這個算法,將“對象是否不再需要”定義為:對象是否可以獲得。

          標記清除算法,是假定設置一個根對象(root),在JS中是全局對象。垃圾回收器定時找所有從根開始引用的對象,然后再找這些對象引用的對象...直到找到所有可以獲得的對象和搜集所有不能獲得的對象。

          它比引用計數垃圾收集更好,因為“有零引用的對象”總是不可獲得的,但是相反卻不一定,參考“循環引用”。

          循環引用不再是問題:

          function fun(){
           var obj1={}, obj2={};
           obj1.leo=obj2; // obj1引用obj2
           obj2.leo=obj1; // obj2引用obj1
           return 'hi pingan';
          }
          fun();
          

          還是這個代碼,可以看出,使用標記清除算法來看,函數調用之后,兩個對象無法從全局對象獲取,因此將被回收。相同的,下面案例,一旦 obj 和其事件處理無法從根獲取到,他們將會被垃圾回收器回收。

          var obj;
          window.onload=function(){
           obj=document.getElementById('myId');
           obj.leo=obj;
           obj.data=new Array(100000).join('');
          };
          

          注意: 那些無法從根對象查詢到的對象都將被清除。

          3.4 個人小結

          在日常開發中,應該注意及時切斷需要回收對象與根的聯系,雖然標記清除算法已經足夠強壯,就像下面代碼:

          var obj,ele=document.getElementById('myId');
          obj.div=document.createElement('div');
          ele.appendChild(obj.div);
          // 刪除DOM元素
          ele.removeChild(obj.div);
          

          如果我們只是做小型項目開發,JS用的比較少的話,內存管理可以不用太在意,但是如果是大項目(SPA,服務器或桌面應用),那就需要考慮好內存管理問題了。

          #

          4.內存泄露(Memory Leak)

          #

          4.1 內存泄露概念

          在計算機科學中,內存泄漏指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存。內存泄漏并非指內存在物理上的消失,而是應用程序分配某段內存后,由于設計錯誤,導致在釋放該段內存之前就失去了對該段內存的控制,從而造成了內存的浪費。 ——維基百科

          其實簡單理解:一些不再使用的內存無法被釋放。

          當內存占用越來越多,不僅影響系統性能,嚴重的還會導致進程奔潰。

          4.2 內存泄露案例

          1. 全局變量

          未定義的變量,會被定義到全局,當頁面關閉才會銷毀,這樣就造成內存泄露。如下:

          function fun(){
           name='pingan';
          };
          
          1. 未銷毀的定時器和回調函數
          2. 如果這里舉一個定時器的案例,如果定時器沒有回收,則不僅整個定時器無法被內存回收,定時器函數的依賴也無法回收:
          var data={};
          setInterval(function(){
           var render=document.getElementById('myId');
           if(render){
           render.innderHTML=JSON.stringify(data);
           }
          }, 1000);
          
          1. 閉包
          var str=null;
          var fun=function(){
           var str2=str;
           var unused=function(){
           if(str2) console.log('is unused');
           };
           str={
           my_str=new Array(100000).join('--');
           my_fun=function(){
           console.log('is my_fun');
           };
           };
          };
          setInterval(fun, 1000);
          

          定時器中每次調用fun,str都會獲得一個包含巨大的數組和一個對于新閉包my_fun的對象,并且unused是一個引用了str2的閉包。

          整個案例中,閉包之間共享作用域,盡管unused可能一直沒有調用,但my_fun可能被調用,就會導致內存無法回收,內存增長導致泄露。

          1. DOM引用 當我們把DOM的引用保存在一個數組或Map中,即使移除了元素,但仍然有引用,導致無法回收內存。例如:
          var ele={
           img : document.getElementById('my_img')
          };
          function fun(){
           ele.img.src="http://www.baidu.com/1.png";
          };
          function foo(){
           document.body.removeChild(document.getElementById('my_img'));
          };
          

          即使foo方法將my_img元素移除,但fun仍有引用,無法回收。

          4.3 內存泄露識別方法

          1. 瀏覽器

          通過Chrome瀏覽器查看內存占用:

          步驟如下:

          • 打開開發者工具,選擇 Timeline 面板
          • 在頂部的Capture字段里面勾選 Memory
          • 點擊左上角的錄制按鈕
          • 在頁面上進行各種操作,模擬用戶的使用情況
          • 一段時間后,點擊對話框的 **stop **按鈕,面板上就會顯示這段時間的內存占用情況

          如果內存占用基本平穩,接近水平,就說明不存在內存泄漏。

          反之,就是內存泄漏了。

          1. 命令行

          命令行可以使用 Node 提供的process.memoryUsage方法。

          console.log(process.memoryUsage());
          // { rss: 27709440,
          // heapTotal: 5685248,
          // heapUsed: 3449392,
          // external: 8772 }
          

          process.memoryUsage返回一個對象,包含了 Node 進程的內存占用信息。該對象包含四個字段,單位是字節,含義如下。

          • rss(resident set size):所有內存占用,包括指令區和堆棧。
          • heapTotal:"堆"占用的內存,包括用到的和沒用到的。
          • heapUsed:用到的堆的部分。
          • external: V8 引擎內部的 C++ 對象占用的內存。

          判斷內存泄漏,以heapUsed字段為準。

          公眾號:前端自習課

          eb服務是.net中讓人激動的部分,幾乎所有你能叫出名字的服務都有一些執行服務器端代碼的機制:正巧每種語言都一個類庫,因此在HTTP中生成一個GET請求變得很簡單,解析出XML也有了些捷徑。

          這種方案給你提供了一種跨平臺,跨語言,跨廠商乃至一切的方法,只要它們都在INTERNET上或是以其他的方式相連,我們就可以在某個程序的代碼中調用另外一個完全不同的機器上的代碼。



          這就是隱藏在WEB服務背后的基本觀念。使用類似于WEB服務描述語言(說 wizdle會更酷一些)開發有一定的標準,它們涵蓋了這些技術細節。

          如果你用Visual Studio.NET創建一個WEB服務,它將滿足這些標準。如果你只是需要WEB服務,而不管它是如何創建的,通過Visual Studio.NET,你會發現借用他人的代碼是如此簡單。

          編寫一個WEB服務

          為了編寫一個WEB服務,你至少要用一種方法寫一個類。這個類必須有WebService屬性,方法也要有WebMethod屬性。WEB方法能夠接受和返回任何可用的類型,包括你定義的對象實例。它們能做任何事情:維護數據庫數據的內外一致性,做任何形式的運算,甚至調用另外一個WEB方法來完成任務。

          在Visual Studio.NET中創建一個新工程。在Visual C++工程模板中,選擇可管理的WEB服務。修改后是:

          <@ WebService Class=Calculator.CalculatorService %>

          我獲得了一個叫做HelloWorld()的方法,把它改成Add()很簡單——我僅僅更改了.cpp文件和.h文件的名稱,改變了簽名以便它能夠接受浮點數,然后加了些代碼以返回和。



          類聲明的結束部分:

          using <System.Web.Services.dll>
          using namespace System;
          using namespace System::Web;
          using namespace System::Web::Services;
          namespace Calculator
          {
          public __gc
          class CalculatorService : public WebService
          {
          public:
          [System::Web::Services::WebMethod]
          double Add(double x, double y);
          };
          }

          實現的部分:

          #include "stdafx.h"
          #include "Calculator.h"
          #include "Global.asax.h"
          namespace Calculator
          {
          double CalculatorService::Add(double x, double y)
          {
          return x + y;
          }
          }

          第一個 CGI 程序

          
          #include <iostream>using namespace std;
          int main (){   
            cout << "Content-type:text/html\r\n\r\n";
            cout << "<html>\n";
            cout << "<head>\n";
            cout << "<title>Hello World - 第一個 CGI 程序</title>\n";
            cout << "</head>\n";
            cout << "<body>\n";
            cout << "<h2>Hello World! 這是我的第一個 CGI 程序</h2>\n";
            cout << "</body>\n";
            cout << "</html>\n";
            return 0;}

          使用WEB服務



          寫一個WEB服務相當簡單:你只需要一個類屬性,一個方法屬性和calculator.asmx文件,而這三個都由Visual Studio生成。

          最簡單的方法是鍵入URL到Calculator.asmx然后按回車。你會看到和以前運行WEB服務工程時同樣的文件。點擊添加參數結束這個過程。



          參數一旦添加,調用WEB服務就像調用任何C++類一樣。添加參數建立一個頭文件,這個文件在任何你想使用WEB服務時都可以包括進去。

          近一直在開源社區瀏覽一些開源的后臺管理框架,從中找到一個自己中意的項目很難,不是后期開發太繁瑣,要不就是界面不友好,想了想還是自己搭建一個后臺管理模板,以后在開發過程中直接拿過來開發就可以了。

          此項目是基于一個HEYUI搭建的一款后臺管理模板。

          HeyUI 是一套基于 Vue2.0 的開源 UI 組件庫,主要服務于一些中后臺產品。

          HeyUI提供的是一整套解決方案,所有的組件提供全局的可配置模式。真正的數據驅動、全局的配置模式、數據字典化

          后臺布局通常采用的上左右布局方式

          第一部分:登錄頁面

          登錄頁面主要是靠設計,設計做的好,前端怎么實現出來都好看,這里的登錄界面也沒有經過專業的設計,反正還能看的過眼吧(請忽略開發的審美)。

          功能點:選擇記住登錄賬號時,在登錄成功后會將賬號保存到本地存儲,下次進入頁面直接從本地存儲獲取登錄賬號,同時在點擊登錄時會將兩個輸入框屬性設置為只讀,按鈕出現加載效果。

          第二部分:管理后臺頭部

          相對于整個系統來說,這部分是最簡單的一部分,其中大多數要寫的只是css樣式,唯一一點就是登錄用戶這塊,用到了vuex狀態管理,如果你在某一處修改了用戶信息,只需要執行

          this.$store.dispatch('userStore/setUserInfo', {name: '超級管理員'})

          就可以修改頁面上顯示的登錄用戶的用戶名。

          第三部分:左邊菜單管理

          使用Menu組件即可,通過key匹配當前選中的菜單,執行

          this.$refs.menu.select(this.$route.name)

          選中當前菜單。

          第四部分:內容區域

          采用子路由加載方式 <router-view />

          第五部分:模板主題配置

          這里使用的是HEYUI的自定義主題,需要自己定義一個less文件做引用,在less文件中定義主題變量,覆蓋框架默認的主題,本模板就是采用自定義less變量更換主題的。

          主題文件位置:src\style\themes.less

          原始全局變量文件你可以在github上查看。

          https://github.com/heyui/heyui/blob/master/themes/var.less

          如果你還需要一些更細節化的調整,官方建議可以新建overwrite.less對已有的class進行覆蓋修改。

          項目地址:https://gitee.com/yangon/vue-admin-frame

          使用

          git clone https://gitee.com/yangon/vue-admin-frame
          
          cd vue-admin-frame
          
          npm install
          
          npm run dev

          本項目引用到的框架KEYUI:https://www.heyui.top


          如果本文對您有所幫助, 請關注科技男給與支持,后期為您帶來更多的技術文章。


          主站蜘蛛池模板: 亚洲欧洲一区二区| 久久精品无码一区二区无码| 亚洲一区二区三区乱码在线欧洲| 理论亚洲区美一区二区三区| 亚洲国产精品一区二区三区久久 | 国产免费一区二区视频| 亚洲AV无码一区二区三区网址| 天天看高清无码一区二区三区 | 国产亚洲一区二区手机在线观看 | 精品人妻一区二区三区四区| 色一乱一伦一图一区二区精品| 国产一区二区三区在线免费| 亚洲国产激情在线一区| 亚洲综合无码AV一区二区| 日韩精品一区二区三区不卡| 亚洲AV成人精品一区二区三区 | 美女福利视频一区| 久久国产三级无码一区二区| 怡红院一区二区三区| 国产福利一区二区三区在线观看| 中文字幕精品一区二区精品| 一区二区三区www| 美女AV一区二区三区| 人妻少妇精品视频三区二区一区 | 久久精品一区二区免费看| 国产成人片视频一区二区| 国产一区二区三区在线免费观看 | 久久婷婷色一区二区三区| 亚洲A∨精品一区二区三区| 免费看AV毛片一区二区三区 | 亚洲日本一区二区三区在线 | 日韩经典精品无码一区| 日韩精品福利视频一区二区三区| 日本片免费观看一区二区| 精品视频无码一区二区三区 | 无码人妻品一区二区三区精99| 国精产品一区二区三区糖心| 中文字幕一区二区三区视频在线| 日本一区二区在线免费观看| 一区二区三区www| 国内国外日产一区二区|