淺談js內(nèi)存

本文是lhyt本人原創(chuàng),希望用通俗易懂的方法來理解一些細節(jié)和難點。轉(zhuǎn)載時請注明出處。文章最早出現(xiàn)于本人github

0.前言

主要結(jié)合了內(nèi)存的概念講了js的一些的很簡單、但是又不小心就犯錯的地方。

結(jié)論:js執(zhí)行順序,先定義,后執(zhí)行,從上到下,就近原則

1.先說類型

在ECMAscript數(shù)據(jù)類型有基本類型和引用類型,基本類型有Undefined、Null、Boolean、Number、String,引用類型有Object,所有的的值將會是6種的其中之一(數(shù)據(jù)類型具有動態(tài)性,沒有定義其他數(shù)據(jù)類型的必要了)

引用類型的值,也就是對象,一個對象是某個引用類型的一個實例,用new操作符創(chuàng)建也可以用字面量的方式(對象字面量創(chuàng)建var obj ={ })。ECMA里面有很多原生的引用類型,就是查文檔的時候看見的那些:Function、Number (是對于原始類型Number的引用類型)、String(是對于原始類型String的引用類型)、Date、Array、Boolean(...)、Math、RegExp等等。

在程序運行的時候,整塊內(nèi)存可以劃分為常量池(存放基本類型的值)、棧(存放變量)、很大的堆(存放對象)、運行時環(huán)境(函數(shù)運行時)

內(nèi)存劃分


對于如下代碼:

var a = 1;

var b = 'hello';

var c = a;

var obj1 = new Object();

obj1.name = 'obj1'

var obj2 = obj1

基本數(shù)據(jù)類型的值是直接在常量池里面可以拿到,而引用類型是拿到的是對象的引用

c = a,這種基本數(shù)據(jù)類型的復制,只是重新復制一份獨立的副本,在變量的對象上創(chuàng)建一個新的值,再把值復制到新變量分配的位置上,a、c他們自己的操作不會影響到對方。

a++;console.log(a);console.log(c)顯然是輸出2、1

obj1和obj2,拿到的是新創(chuàng)建的對象的引用(也就是家里的鑰匙,每個人帶一把),當操作對象的時候,對象發(fā)生改變,另一個obj訪問的時候,發(fā)現(xiàn)對象也會改。就像,家里有一個人回去搞衛(wèi)生了,另一個回家發(fā)現(xiàn)家里很干凈了。

console.log(obj2) //'obj1'


基本類型和引用類型

函數(shù)也是同理

var a = function(){console.log(1)}

var b = a;

a = null;

b();a()

//b輸出1,a報錯:Uncaught TypeError: a is not a function

把a變成null,只是切斷了a和函數(shù)之間的引用關(guān)系,對b沒有影響

2.再說順序

大家常聽說的先定義后執(zhí)行,其實就是在棧中先開辟一塊內(nèi)存空間,然后在拿到他所對應的值,基本類型去常量池,引用類型去堆拿到他的引用。大家常說的原始類型值在棧,其實就是這種效果。

基本類型和引用類型取值順序

2.1 為什么引用類型值要放在堆中,而原始類型值要放在棧

棧比堆的運算速度快,Object是一個復雜的結(jié)構(gòu)且可以擴展:數(shù)組可擴充,對象可添加屬性,都可以增刪改查。將他們放在堆中是為了不影響棧的效率。而是通過引用的方式查找到堆中的實際對象再進行操作。

因此又引出另一個話題,查找值的時候先去棧查找再去堆查找。為什么先去棧查找再去堆查找

簡單來說,你寧愿大海撈針呢還是碗里撈針呢?

具體如何,還得問一下語言的底層去了

3.函數(shù)

先拋出一個問題

function a(){console.log(2)}; var a = function(){console.log(1)}; a()

覆蓋?那么交換的結(jié)果又是什么呢?

var a = function(){console.log(1)}; function a(){console.log(2)}; a()

都是1,然后有的人就說了,var優(yōu)先。好的,那為什么var優(yōu)先?


先定義后執(zhí)行,先去棧查找

變量提升,其實也是如此。先定義(開辟一塊內(nèi)存空間,此時值可以說是undefined)后執(zhí)行(從上到下,該賦值的就賦值,該執(zhí)行操作的就去操作),就近原則

函數(shù)聲明和函數(shù)表達式,有時候不注意,就不小心出錯了

a(); function a(){console.log(666)}//666

另一種情況:

a(); var a = function (){console.log(666)}//a is not a function

雖然第一種方法有變量提升,不會出錯,正常來說,還是按順序?qū)?,定義語句放前面。如果想嚴格要求自己,就手動來個嚴格模式‘use strict’吧。對于框架的開發(fā),需要嚴謹遵守規(guī)則,所以一般會用嚴格模式。

4.接著是臨時空間

函數(shù)執(zhí)行的時候,會臨時開辟一塊內(nèi)存空間,這塊內(nèi)存空間長得和外面這個一樣,也有自己的棧堆,當函數(shù)運行完就銷毀。

4.1 eg1:

var a = 10;

function() {

console.log(a);//undefined

var a = 1;

console.log(a)//1

}

宏觀來說,只有2步一和二,當執(zhí)行第二步,就跳到函數(shù)內(nèi)部執(zhí)行②-⑧

函數(shù)外部的a=10完全就沒有關(guān)系,這里面造成undefined主要因為變量提升,其實準確的順序是:

var a

console.log(a);//undefined

a = 1;

console.log(a)//1

為什么不出去找全局的a?

就近原則。為什么就近原則?都確定函數(shù)內(nèi)部有定義了,就不會再去外面白費力氣,說到底這個還是得問一下底層怎么實現(xiàn)。類似的一個例子,我們用函數(shù)聲明定義一個函數(shù)f,再用一個變量g拿到這個函數(shù)的引用,然后在外面用f是訪問不了這個函數(shù)的,但是在函數(shù)內(nèi)部是能找到f這個名字的:

var g = function f(){}

f()//報錯

我猜想是,這是內(nèi)部的一種性能優(yōu)化方法,他不會浪費更多的資源去干一件事,具體是什么原因,為什么就近原則,得問底層原理去了。

4.2 eg2

function f(){

return function f1(){

console.log(1)

}

};

var res = f();

res();

f1()

res(),返回的是里面的函數(shù),如果直接f1()就報錯,因為這是window.f1()

函數(shù)聲明后,可以通過引用名稱查找或者內(nèi)存地址查找

局部作用域用function聲明,聲明不等于創(chuàng)建,只有調(diào)用函數(shù)的時候才創(chuàng)建

函數(shù)f有內(nèi)存地址的話,通過棧找f的內(nèi)存空間,如果找不到棧中f這個變量,就去堆中找

5.IIFE和閉包

5.1 IIFE

立即執(zhí)行函數(shù),內(nèi)部就是一個閉包,形成一個沙盒環(huán)境,防止變量污染內(nèi)部,是做各種框架的好方法

先手寫一段假的jQuery

(function(root){

var $ = function(){

//代碼

}

root.$ = $

})(this)

這樣子在內(nèi)部函數(shù)里面寫相關(guān)的表達式,我們就可以用美元符號使用jQuery(實際上jQuery第一個括號是全局環(huán)境判斷,真正的函數(shù)體放在第二個括號里面,號稱世界上最強的選擇器sizzle也里面)

5.2閉包

閉包的概念各有各的說法,平時人家問閉包是什么,大概多數(shù)人都是說函數(shù)中的函數(shù)、函數(shù)里面能訪問到外面的變量。

《權(quán)威指南》:函數(shù)對象通過作用域鏈相互關(guān)聯(lián)起來,函數(shù)內(nèi)部變量都可以保持在函數(shù)的作用域中,有權(quán)訪問另一個函數(shù)作用域中的變量

《忍者秘籍》:一個函數(shù)創(chuàng)建時允許自身訪問并操作該自身函數(shù)以外的變量所創(chuàng)建的作用域

《你不知道的js》:是基于詞法的作用域書寫代碼時所產(chǎn)生的結(jié)果,當函數(shù)記住并訪問所在的詞法作用域,閉包就產(chǎn)生了

其實這是閉包的現(xiàn)象。閉包有現(xiàn)象與產(chǎn)生。閉包的產(chǎn)生,會導致內(nèi)存泄漏。

js具有垃圾回收機制,如果發(fā)現(xiàn)變量被不使用將會被回收,而閉包相互引用,讓他不會被回收,一直占據(jù)著一塊內(nèi)存,長期持有一塊內(nèi)存的引用,這就是內(nèi)存泄漏


原文來源于:lhyt的github

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,569評論 0 13
  • 工廠模式類似于現(xiàn)實生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實現(xiàn)同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 8,131評論 2 17
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,256評論 0 38
  • 單例模式 適用場景:可能會在場景中使用到對象,但只有一個實例,加載時并不主動創(chuàng)建,需要時才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,320評論 1 10
  • 當滿世界新綠,發(fā)散昂揚生機的季節(jié),看那微風中搖曳枝條的桑樹時,常常不由自主地想起三年前那場養(yǎng)蠶的經(jīng)歷。那是一段合...
    書香精靈閱讀 650評論 4 3

友情鏈接更多精彩內(nèi)容