javascript數據類型分爲基本數據類型,引用數據類型和Symbol。javascript
基本數據類型
String,Number,Boolean,undefined,nulljava
引用數據類型
Object
這裏的Object包括帶編號的有序集合Array,包含key/value的無序集合和另外一種特殊對象Function。算法
要點:基本數據類型的值是不可變的,引用數據類型值是可變的
基本數據類型的複製
基本數據類型變量複製是分配新的地址,新值是被複制變量的一個副本,變量之間是獨立的 ,互不影響。數組
var num1 = 5; var num2 = num1;
引用數據類型的複製
引用數據類型複製時候也會分配新的地址,不一樣的是新地址中存儲的是引用數據在堆內存中的指針。複製操做結束後,二者都指向同一個引用地址。所以,修改其中一個會影響另一個。瀏覽器
var obj1 = new Object(); var obj2 = obj1; obj1.name = "Nicholas"; alert(obj2.name); //"Nicholas"
類型檢測
一、typeof閉包
typeof是操做符不是函數。使用typeof會返回以下值:ide
注意:在使用typeof檢測數組和null都會返回object,所以typeof並不能精確判斷某一數據類型。
二、instanceof
若是某個變量是指定引用類型的實例,則instanceof會返回true。固然數組的判斷可使用Array.isArray()。函數
注意:instanceof也並不是徹底可靠,1.因爲變量的原型並非一層不變的,一旦原型被修改,就可能返回false。2.沒法判斷多全局對象,好比在多窗口之間進行原型判斷,多窗口意味着多全局對象,擁有不一樣的內置構造函數,好比[] instanceof window.frames[0].Array會返回false
三、constructor
「javascript中一切都是對象」,全部對象都會在其原型上繼承一個constructor屬性指向其構造函數。
spa
注意:null和undefined沒有constructor。一樣,對象的constructor也是能夠改變的,好比:
var a=[]; a.constructor=new Number(); console.log(a.constructor);//Number
四、Object.prototype.toString操作系統
Object.prototype.toString會返回一個表示該對象的字符串
Object.prototype.toString.call()
JavaScript執行環境決定了變量或函數是否有權訪問,每一個執行環境都有一個變量對象(variable object),執行環境中全部變量和函數都保存在該對象中,雖然咱們沒法直接訪問該對象,JavaScript解析器執行時會使用到它。某個執行環境全部代碼執行完畢後隨之銷燬。
全局執行環境
全局執行環境是最外圍的一個執行環境,在瀏覽器中,全局執行環境被認爲是window對象,全部變量和函數都是window下的屬性或者方法。
局部執行環境
每一個函數都有本身的局部執行環境,當執行流進入函數時候,函數的執行環境就會被推入到執行棧中,函數執行完畢後就會被彈出執行棧。函數的參數也是局部變量,其優先級高於外部變量。
函數做用域和變量聲明提高
每一個函數內部變量只能在函數體內訪問,函數外是無權訪問的,包括函數的參數。
函數內定義的變量在整個函數體內均可訪問,即便變量聲明在變量使用以後,這就是變量聲明提高。最典型的例子以下:
var a=1; function test(){ console.log(a);//1 a=2; console.log(a);//2 } test();
換一下
var a=1; function test(){ console.log(a);//undefined var a=2; console.log(a);//2 } test();
for(var i=0;i<10;i++){ console.log(i);//輸出1~9 } console.log(i);//輸出10
ES6以前JavaScript沒有塊級做用域
理解這句話能夠看以下例子:
function test(o){ if(typeof o==='object'){ var i="test"; for(var k=0;k<10;k++){ console.log(k); } console.log(k) } console.log(i); } test();//undefined test({});//輸出0~9,10,test
可見,即便加了if條件判斷或者循環,變量i和k均可以在大括號代碼塊外訪問。
做用域鏈
每段JavaScript代碼或者函數都有一個與之關聯的做用域鏈(scope chain),這個做用域鏈是一個對象列表或者鏈表,這組對象定義了這段代碼「做用域」中的變量。
當JavaScript須要查找變量a的時候,它會從鏈中的第一個對象開始查找,若是這個對象包含變量a屬性,則會直接使用該對象中的a屬性,若是不存在,則繼續向上查找第二個對象,若是第二個對象也沒有,則繼續查找下一個,以此類推。若是整個做用域鏈上都沒有a屬性,則會拋出異常。
注意:在JavaScript頂層代碼中,做用域鏈由一個全局對象組成,在函數體內,做用域鏈有2個,一個是定義函數參數和函數局部變量的對象,一個是全局對象,若是函數內找不到某個變量,會繼續在全局對象中查找。
聲明變量就得在內存中給它分配存儲地址,基本數據類型存儲在棧中,引用數據類型存儲在堆中。
內存的生命週期
內存的分配
let myNumber = 23
JavaScript在執行上面代碼時候流程以下:
基本數據類型的複製是分配新的地址,是被複制對象的副本,所以會互不干擾;
引用數據類型的複製是堆地址指針的複製,複製後它們指向同一地址,所以修改其中一個會影響另一個。
內存的使用
在JavaScript中使用分配的內存意味着在其中讀寫,這能夠經過讀取或寫入變量或對象屬性的值,或者將參數傳遞給函數來實現。
內存釋放
這裏最困難的地方是肯定什麼時候再也不須要分配的內存,它一般要求開發人員肯定程序中哪些地方再也不須要內存的並釋放它。
垃圾收集
引用計數
這是最簡單的垃圾收集器算法。若是沒有引用指向這個對象的時候,這個對象就被認爲是「能夠做爲垃圾收集」。
var o = { a: { b:2 } }; // 兩個對象被建立,一個做爲另外一個的屬性被引用,另外一個被分配給變量o // 很顯然,沒有一個能夠被垃圾收集 var o2 = o; // o2變量是第二個對「這個對象」的引用 o = 1; // 如今,「這個對象」的原始引用o被o2替換了 var oa = o2.a; // 引用「這個對象」的a屬性 // 如今,「這個對象」有兩個引用了,一個是o2,一個是oa o2 = "yo"; // 最初的對象如今已是零引用了 // 他能夠被垃圾回收了 // 然而它的屬性a的對象還在被oa引用,因此還不能回收 oa = null; // a屬性的那個對象如今也是零引用了 // 它能夠被垃圾回收了
循環引用的問題
當遇到循環的時候就會有一個限制。在下面的實例之中,建立兩個對象,而且互相引用,所以就會產生一個循環。當函數調用結束以後它們會走出做用域以外,所以它們就沒什麼用而且能夠被釋放。可是,基於引用計數的算法認爲這兩個對象都會被至少引用一次,因此它倆都不會被垃圾收集器收集。
function f(){ var o = {}; var o2 = {}; o.a = o2; // o 引用 o2 o2.a = o; // o2 引用 o return "azerty"; } f();
標記清除
JavaScript 中最經常使用的垃圾收集方式是標記清除(mark-and-sweep)。當變量進入環境(例如,在函數中聲明一個變量)時,就將這個變量標記爲「進入環境」。從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,由於只要執行流進入相應的環境,就可能會用到它們。而當變量離開環境時,則將其標記爲「離開環境」。這個算法由如下步驟組成:
四種常見的JavaScript泄露
1. 全局變量
一個未聲明變量的引用會在全局對象內部產生一個新的變量。在瀏覽器的狀況,這個全局變量就會是window。
function foo(arg) { bar = "some text"; } 等同於: function foo(arg) { window.bar = "some text"; }
2.被遺忘的計時器和回調
setInterval 在 JavaScript 中是常常被使用的。大多數提供觀察者和其餘模式的回調函數庫都會在調用本身的實例變得沒法訪問以後對其任何引用也設置爲不可訪問。 可是在setInterval的狀況下,這樣的代碼很常見
var serverData = loadData(); setInterval(function() { var renderer = document.getElementById('renderer'); if(renderer) { renderer.innerHTML = JSON.stringify(serverData); } }, 5000); //每5000ms執行一次
renderer所表明的對象在將來可能被移除,讓部分interval 處理器中代碼變得再也不被須要。然而,這個處理器不可以被收集由於interval依然活躍的(這個interval須要被中止從而表面這種狀況)。若是這個interval處理器不可以被收集,那麼它的依賴也不可以被收集。這意味這存儲大量數據的severData也不可以被收集。
3. 閉包
閉包的特性是內部函數可以訪問外部函數的做用域。
var sayName = function(){ var name = 'jozo'; return function(){ alert(name); } }; var say = sayName(); say();
sayName返回了一個匿名函數,該函數又引用了sayName的局部變量name,sayName 調用後變量name應該被回收,可是因爲say繼續引用,致使沒法回收。
小結: 一、JavaScript基本數據類型:string,number,boolean,null,undefined,引用類型,包括Object,Array,function,ES6新增的symbol。 二、判斷數據類型的方法有typeof,instanceof,constructor,Object.prototype.toString。 三、JavaScript分爲全局做用域和局部做用域,做用域鏈向上層層查找。 四、基本數據類型佔據固定大小空間,所以存儲在棧內存中,引用類型佔據空間不肯定,存儲在堆內存中。複製基本數據類型會分配新地址,新舊互不影響,引用類型複製是複製指針,新舊變量會互相影響。 五、JavaScript垃圾回收方法包括引用計數和標記清除。 六、JavaScript常見的內存泄漏包括全局變量,循環引用,計時器,閉包。