1. 程式人生 > >原生js的一些研究和總結(1)

原生js的一些研究和總結(1)

資料型別

基本型別值包括: undefined,null,Boolean,Number和String,這些型別分別在記憶體中佔有固定的大小空間,它們的值儲存在棧空間,我們通過按值來訪問的。
引用型別包括:物件、陣列、函式等。
對於引用型別的值,則必須在堆記憶體中為這個值分配空間。由於引用型別值的大小不固定(物件有很多屬性和方法,而且還可以動態的新增屬性和方法),因此不能把他們儲存到棧記憶體中。但記憶體地址大小是固定的,因此可以將記憶體地址儲存在棧記憶體中

簡而言之:棧記憶體中存放的是基本資料型別值,堆記憶體中存放的是引用型別值,引用型別值在記憶體中的地址存放在棧中,也就是我們常說的物件物件引用(指標)。
這裡寫圖片描述

變數複製

<script type="text/javascript">
var a = 5;
var b = a;
console.log(a+"---"+b);//5---5
b = 6;//這裡重新給b賦值,a值並沒有改變
console.log(a+"---"+b);//5---6

var obj = {name:"lisi"};
var obj2 = obj;//這裡是引用賦值,obj和obj2指向同一個物件
console.log(obj.name + "---" + obj2.name);//lisi---lisi
obj2.name = "wangwu";
console.
log(obj.name + "---" + obj2.name);//wangwu---wangwu </script>

從上面例子可以看出:在變數複製方面,基本型別和引用型別也有所不同,基本型別複製的是值本身,而引用型別複製的是記憶體地址。

函式

this 的工作原理(5種情況)

全域性作用域內

當在全部作用域內使用 this時,它將會指向全域性物件,即window物件

函式呼叫

擋在全域性作用域內呼叫函式時,this 也會指向全域性物件。

方法呼叫

this 指向呼叫該方法的物件。

呼叫建構函式

在建構函式內部,this 指向新建立的物件。

顯式的設定this指向

當使用call 或者 apply 方法時,函式內的 this 將會被顯式設定為函式呼叫的第一個引數。
更詳細用法請見之前的部落格:傳送門

匿名函式

在javascript裡任何匿名函式都是屬於window物件。在定義匿名函式時候它會返回自己的記憶體地址,如果此時有個變數接收了這個記憶體地址,那麼匿名函式就能在程式裡被使用了(可以通過在這個變數後面加一對圓括號來呼叫這個匿名函式),因為匿名函式也是在全域性執行環境構造時候定義和賦值,所以匿名函式的this指向也是window物件。

<script type="text/javascript">
(function(){
    console.log(this == window);//true
})();
</script>

引數傳遞

<script type="text/javascript">
     function test(num){//按值傳遞
         num+=5;
         console.log(num);
         return num;
     }
     var num = 5;
     var result = test(num);
     console.log(result);// 10 如果是按引用傳遞,那麼函式裡的num會成為類似全域性變數,把外面的num覆蓋掉
     console.log(num);// 5 也就是說,最後應該輸出20(這裡輸出10)
</script>

js中不存在引用傳遞,如果存在引用傳遞的話,那麼函式內的變數將是全域性變數,在外部也可以訪問,但這明顯是不可能的。
再看一個例子:

<script type="text/javascript">
function setName(obj){//obj = person
    obj.name = "lisi";
    obj = new Object();
    obj.name = "wangwu";
}
var person = new Object();
setName(person);
alert(person.name);
console.log(obj.name);//Uncaught ReferenceError: obj is not defined
</script>

在將person傳遞給obj後,其name屬性就被設定成了”lisi”。又將obj重新定義了一個物件,另一行程式碼為該物件定義了一個帶有不同值的name屬性。
如果person是按引用傳遞的,那麼person就會自動被修改為指向其name屬性值為”wangwu”的新物件,但事實上並沒有,其name屬性依然是”lisi”。
這就說明:即使在函式內部修改了引數的值,但原始的引用仍然保持不變。實際上,當在函式內重寫obj時,這個變數引用的就是一個區域性物件了。而這個區域性物件會在函式執行完畢後立即被銷燬。

執行環境及作用域

執行環境

執行環境定義了變數或函式有權訪問其他資料,可以分為全域性執行環境和區域性執行環境。
全域性執行環境是最外層的執行環境,在瀏覽器中,全域性執行環境是window物件,因此,所有的全域性變數的函式都是作為window的屬性和方法建立的。
注意:對於區域性執行環境,其內部的程式碼執行完畢後,該環境將被銷燬,儲存其中的變數和函式也隨之銷燬,如果是全域性執行環境,需所有程式執行完畢或網頁完畢後才會銷燬。

需要注意的小細節

去掉var的區域性變數
<script type="text/javascript">
      var name = "wangwu";
      function setName(){
          name = "lisi";//去掉var變成了全域性變數,會覆蓋全域性中的name
      }
      setName();
      console.log(name);//lisi
</script>
形參也是區域性變數
<script type="text/javascript">
      var name = "wangwu";
      function setName(name){//形參也是區域性變數
          console.log(name);
      }
      setName("lisi");//lisi
      console.log(name);//wangwu
</script>

作用域

當代碼在一個環境中執行的時候,就會形成作用域鏈,它的作用是保證對執行環境中有訪問許可權的變數和函式進行有序訪問(內部深層環境可以訪問外層環境,反之不成立),作用域鏈的前端,就是執行環境的變數物件。
詳見之前部落格:傳送門

記憶體洩漏常見情況

javascript具有自動垃圾回收機制,一旦資料不再使用,可以將其設為”null”來釋放引用。

迴圈引用

分為兩種情況:不同物件之間相互引用和
一個經典的例子:一個DOM物件被一個Javascript物件引用,與此同時又引用同一個或其它的Javascript物件,這個DOM物件可能會引發記憶體洩露。這個DOM物件的引用將不會在指令碼停止的時候被垃圾回收器回收。要想破壞迴圈引用,引用DOM元素的物件或DOM物件的引用需要被賦值為null。

<body>
<script type="text/javascript">
var oBox = document.getElementById("box");
var obj = {};
oBox.name = obj;
obj.age = oBox;//這樣就發生了迴圈引用
</script>
</body>

閉包(常見)

通過閉包引用其包含函式的區域性變數,當閉包結束後該區域性變數無法被垃圾回收機制回收,造成記憶體洩漏。

DOM洩漏

當原有的DOM被移除時,子結點引用沒有被移除則無法回收。

var select = document.querySelector;
var treeRef = select('#tree');
//在COM樹中leafRef是treeFre的一個子結點
var leafRef = select('#leaf'); 
var body = select('body');
body.removeChild(treeRef);
//#tree不能被回收入,因為treeRef還在
//解決方法:
treeRef = null;
//tree還不能被回收,因為葉子結果leafRef還在
leafRef = null;
//現在#tree可以被釋放了。

定時器洩漏

for (var i = 0; i < 1000; i++) {
  var obj = {
    fn: function() {
      var that = this;
      var val = setTimeout(function() {
        that.fn();
      }, 500);
    }
  }
  obj.fn();
  //雖然你想回收但是timer還在
  obj = null;
}

垃圾回收

標記清除

js中最常用的垃圾回收方式就是標記清除。垃圾收集器在執行的時候回給儲存在記憶體中的所有變數都加上標記。然後,它會去掉環境中的變數以及被環境中的變數引用的變數的標記(閉包)。而在此之後再被加上標記的變數將被視為準備刪除的變數,原因是環境中的變數已經無法訪問到這些變量了。最後,垃圾收集器完成記憶體清除工作,消除那些帶標記的值並回收它們所佔用的記憶體空間。
例如:在函式中宣告一個變數,就將這個變數標記為”進入環境”。從邏輯上講,永遠不能釋放進入環境的變數所佔用的記憶體,因為只要執行流進入相應的環境,就可能會用到它們。而當變數離開環境時,則將其標記為”離開環境”。

function test(){
 var x = 10 ; //被標記 ,進入環境 
 var y = 20 ; //被標記 ,進入環境
}
test(); //執行完畢後 x、y又被標離開環境,被回收。

引用計數

引用計數就是:跟蹤記錄每個值被引用的次數。當聲明瞭一個變數並將一個引用型別值賦給該變數時,則這個值的引用次數就是1。如果同一個值又被賦給另一個變數,則該值的引用次數加1。相反,如果包含對這個值引用的變數又取得了另外一個值,則這個值的引用次數減1。當這個值的引用次數變成0時,則說明沒有辦法再訪問這個值了,因而就可以將其佔用的記憶體空間回收回來。這樣,當垃圾回收器下次再執行時,它就會釋放那些引用次數為0的值所佔用的記憶體

function test(){
 var x = {} ; //x的引用次數為0 
 var y = x ; //x的引用次數加1,為1 
 var z =x; //x的引用次數再加1,為2
 var y ={}; //x的引用次數減1,為1
}
引用計數策略的問題(迴圈引用)
function test() {
 var a = {};
 var b = {};
 a.name = b;
 b.name = a;
}
test();

以上程式碼a和b的引用次數都是2,fn()執行完畢後,兩個物件都已經離開環境,在標記清除方式下是沒有問題的,但是在引用計數策略下,因為a和b的引用次數不為0,所以不會被垃圾回收器回收記憶體,如果fn函式被大量呼叫,就會造成記憶體洩露。在IE7與IE8上,記憶體直線上升。
我們知道,IE中有一部分物件並不是原生js物件。例如,其BOM和DOM中的物件就是使用C++以COM物件的形式實現的,而COM物件的垃圾回收機制採用的就是引用計數策略。因此,即使IE的js引擎採用標記清除策略來實現,但js訪問的COM物件依然是基於引用計數策略的。換句話說,只要在IE中涉及COM物件,就會存在迴圈引用的問題

var element = document.getElementById("some_element");
var myObject = new Object();
myObject.element = element;
element.someObject = myObject;//產生了迴圈引用

這個例子在一個DOM元素(element)與一個原生js物件(myObject)之間建立了迴圈引用。其中,變數myObject有一個名為element的屬性指向element物件;而變數element也有一個someObject屬性指向myObject。由於存在這個迴圈引用,即使例子中的DOM從頁面中移除,它也永遠不會被回收。

myObject.element = null;
element.someObject = null;

為了避免類似這樣的迴圈引用問題,最好在不使用它們的時候手工斷開原生js物件與DOM元素之間的連線。將變數設定為null意味著切斷變數與它此前引用的值之間的連線。當垃圾收集器下次執行時,就會刪除這些值並回收它們佔用的記憶體。
注意:為了解決上述問題,IE9把BOM和DOM物件都轉換成了真正的js物件。這樣,就避免了兩種垃圾收集演算法並存導致的問題,也消除了常見的記憶體洩漏現象。

相關推薦

原生js一些研究總結(1)

資料型別 基本型別值包括: undefined,null,Boolean,Number和String,這些型別分別在記憶體中佔有固定的大小空間,它們的值儲存在棧空間,我們通過按值來訪問的。 引用型別包括:物件、陣列、函式等。 對於引用型別的值,則必須在堆記

ajax總結(四):原生js封裝ajQurey版ajax介紹

一.為什麼要封裝? 發現很多地方都要用ajax請求,但是大部分程式碼都是一樣的,所以根據封裝的思想,相同的程式碼封裝成函式,在需要用的地方來呼叫,這樣會很方便. 二.js封裝ajax過程: 1.先了解結構程式碼和後臺程式碼: 結構: 後臺: 3.開始封裝函式

程序PM的一些感慨總結

任務 data style 第一次 pop article 手寫 程序猿 span 曾經沒做PM時認為PM是個非常舒服的位置,驗收進度,分工模塊,劃分時間,工資還比PG高。做了幾年PM了,當中甘苦自知。 1、工作任務不一樣,做PG僅僅要把自己的工作模塊非常美麗的完畢,就

js,vue.js一些方法的總結

nbsp 進行 for submit 多個 targe tag spl 修飾 push() 可向數組的末尾添加一個或多個元素 pop() 刪除並返回數組的最後一個元素 shift()刪除並返回數組的第一個元素 unshift() 添加並返回數組的第一個元素 sort()對數

Hybrid APP混合開發的一些經驗總結

後臺 機制 獲取 功能 前端 如果 導致 接口 編寫 寫在前面: 由於業務需要,接觸到一個Hybrid APP混合開發的項目。當時是第一次接觸混合開發,有一些經驗和總結,歡迎各位一起交流學習~ 1、混合開發概述 Hybrid App主要以JS+Native兩者

js一些常用方法總結

  這兩天開始在牛客網上做一些js線上程式設計,發現很多程式設計題其實呼叫的js方法都差不多一樣,所以覺得可以彙總一下,方便記憶也可以多多熟悉。   1.slice()方法     這個方法就是可以從已有的陣列中返回選定的元素。     必須得有start,但是可以沒有end。     2

js:作用域總結1

先說幾個概念:    1、js程式碼從上往下執行     2、變數提升:      變數提升是瀏覽器的一個功能,在執行js程式碼之前,瀏覽器會給js一個全域性作用域叫window,window分兩個模組,一個叫記憶體模組,一個叫執行模組,記憶體模組找到當前作用域下的所有帶var和function的關鍵字

leetcode刷題——一些演算法技巧總結1.0

運算子優先順序,簡單記就是:! > 算術運算子 > 關係運算符 > && > || > 賦值運算子 把數字取反,可以作為一種標記 pythonlast = {c: i for i, c in enumerate(S)}標記字串S中每個字母的最後一個位

遊戲行業入行10年一些思考總結

         從08年開始來上海,進入網路遊戲行業已經第10個年頭了,近期開始有點迷茫,原因可能來自比較多個方面:也許是年齡的慢慢增長,還有今年負責的專案到了生命週期後期業績的下降以及公司一些人事的變動,還有家庭的一些事情的牽掛,還有對於未來規劃的一些思考,基於以上一

分散式事務一些思考總結

最近在做交易訂單的事情,隨著系統越來越複雜,交易量越來越大,出現不一致的情況也頻發。不禁引人思考,這裡做一些總結記錄。分散式事務有一些理論,最常聽的是CAP,ACID等。這裡就不做介紹了,大家自己去查資料看看。分散式事務也經常提到2PC,3PC,因為不太適用網際

原生JS實現輪播圖1---勻速動畫

                                JS實現輪播圖 專案實現結果圖: 需求:1 根據圖片動態新增小圓點            2 目標移動到小圓點輪播圖片            3 滑鼠離開圖片,定時輪播圖片;滑鼠在圖片上時暫

原生JS、jQueryVUE中的Ajax比較

Ajax:不重新整理瀏覽器的情況下載入資料 1.原生JS中的Ajax function loadXMLDoc() { var xmlhttp; if (window.XMLHttpRequest) { // IE7+, Firefox, Chrome, Opera, Safari

關於執行緒一些知識點總結

今天剛剛學習到,人生需要積累,於是把自己每天的學習到的都記錄起來,相信有一天會成為自己寶貴的財富,廢話不多說 直接貼知識點: 1.何為程序 在Android中的App通常會執行在一個獨立的程序中,通常會在正在執行的程式理解為一個程序 每一個程序都有一個

Android關於VideoView的一些思考總結

最近在做一期有關於視訊的專案開發。因為專案週期比較緊,而且自己以前也沒有對於視訊相關的經驗積累。所以在開發的時候遇到了問題,自己嘗試解決並記錄下來。(PS:視訊都是下載完再進行播放類似於微信朋友圈的視訊功能,播放元件用的是分裝的原生的VideoView) 一

Java中類物件的一些心得總結

2014-10-21 在iteye寫的文章,那時候才大三,現在研一。還是想把當時的部落格摘抄過來。 原地址: 學習Java語言也有一段時間了,現在回過頭來總結一下最基本的類和物件的知識。 之前大一上就接觸了C語言,大二上的時又候花了一學期學習了C++,如

原生js實現fadein fadeout淡入淡出效果

用法: $("button").click(function(){ $("p").fadeOut(); }); fadeOut() 方法逐漸改變被選元素的不透明度,從可見到隱藏(褪色效果)。 註釋:隱藏的元素不會被完全顯示(不再影響頁面的佈局)。 提示:該方法通常與 fad

程式PM的一些感慨總結

以前沒做PM時覺得PM是個很舒服的位置,驗收進度,分工模組,劃分時間,工資還比PG高。做了幾年PM了,其中甘苦自知。 1、工作任務不一樣,做PG只要把自己的工作模組很漂亮的完成,就是一個很高的績效,考慮的東西相對較少,能很專注的投入, 做PM你得為程式設計師分工,分模組,還

最近面試的一些經驗總結,希望給跳槽的朋友一些借鑑

  每個人的職業生涯中都會經歷幾次跳槽,說道到槽,第一次跳槽往往大家都沒什麼經驗,導致跳槽的結果不是很好。我也在這方面不是很足,於是就不停的憂鬱和糾結,於選擇哪一種型別的公司中間,但是始終是會選擇一家的,我的選擇標準就是從對自身發展上來看和薪水等綜合表現來選擇。     

2020年的一些思考總結

首先這篇文章不等於”2020年總結”,因為2020年還沒有完全結束,還有近兩個多月的時間。通常來說,我的2020年總結會在2021年初以及2020年末寫的,內容格式都是今年計劃完成情況+來年計劃制定等。 國慶假期遊山玩水了四五天,該玩的都玩了,沒玩的,等有時間了再計劃。 每年在某個特定

一些常用JS函數技巧總結

常用 ces 函數 ont 布爾 gin 技巧 int() js函數 1.JS原生函數parseInt(),返回字符串的第一個數字,默認是十進制。 2.!!data.success //強制轉換成布爾類型 p.p1 { margin: 0.0px 0.0px 0.0px