參考 高性能javascript Tom大叔深刻理解javascript系列javascript
相關概念html
1.執行上下文java
當控制器轉到ecmascript可執行代碼的時候,就會進入一個執行上下文,執行上下文是以堆棧的方式進行管理的,也就是最底層是全局的上下文,最頂層是當前的執行上下文,每當進入function(包括遞歸調用)或者eval,都會產生執行上下文壓入堆棧,隨着函數或者eval的結束,對應的執行上下文被彈出.每當遇到return語句的時候就會推出當前的執行上下文,代碼執行完畢後,管理執行上下文的堆棧只會包含全局的上下文緩存
eval('var x = 10'); (function foo() { eval('var y = 20'); })(); alert(x); // 10 alert(y); // "y" 提示沒有聲明
eval在執行的時候會產生調用上下文,而eval的執行會影響到這個調用上下文的狀態(數據) ecmascript
上面的例子中 第一個eval執行的時候會產生調用上下文,它的調用上下文是全局上下文,也就是這個eval操做會對全局上下文產生影響,第二個eval的調用上下文是函數foo的執行上下文,這個eval操做只會影響到foo的指向上下文,當foo函數執行完畢,foo的執行上下文彈出堆棧,因此在全局上下文中y並無定義函數
2.變量對象性能
變量對象是管理執行上下文相關變量的一種機制,經過變量對象咱們能知道如何訪問上下文對象中的變量,它是一個與執行上下文相關的特殊對象,它儲存着執行上下文中的變量 函數聲明 函數形參 this
在全局的上下文中全局對象自身就是變量對象(理解 由於全局上下文中的變量對象就是全局對象,聲明在全局的變量至關於向全局上下文的變量對象中添加屬性,也就是向全局對象中添加屬性 )spa
在函數上下文中,變量對象這個概念被活動對象所替換,在進入函數上下文的時刻活動對象經過arguments屬性初始化code
function test(x) { console.log(x); console.log(arguments[0]); arguments[0] = 100; console.log(x); } test(1);//形參 和 arguments 之間的數據共享
處理上下文代碼分爲兩個階段,進入執行上下文 執行代碼
進入執行上下文階段 變量對象包含下列的值 函數全部的形式參數(鍵值對) 全部函數聲明 全部的變量聲明(不會干擾在變量對象中存在的同名字的形式參數和函數聲明)
代碼執行階段 其實就是對進入執行上下文階段初始化值的賦值過程
理解函數聲明的提高
alert(x); // function var x = 10; alert(x); // 10 x = 20; function x() { console.log(1); };
在進入執行上下文的階段,變量的聲明是在函數的形式參數聲明和函數聲明以後的,而且變量的聲明不會影響函數的聲明,因此在進入執行上下文階段的變量對象中x的值是指向聲明的函數 在執行階段在對變量的值進行修改
3.變量
變量只能經過var關鍵字進行聲明
a = 10; var b = 10;
在上面的代碼中進入執行上下文階段 變量對象中只包含b undefined 不存在a 之因此存在一種觀點任何不經過var聲明的變量都會轉變成全局變量 是由於在代碼執行階段 好比執行到 a = 10的時候,因爲此時的變量對象就是全局對象,而且咱們訪問全局對象的屬性的時候是能夠省略全局對象的前綴的(也就是this或者window),因此這句話至關於在全局對象中添加屬性,並無聲明一個變量 屬性是能夠刪除的 可是變量是不能夠刪除的 特例
eval("var a = 10;"); console.log(delete a);//true
var b = 10;
console.log(delete b);//false
3 做用鏈域
在每一個執行上下文中都有一個變量對象(函數執行上下文活動對象),做用鏈域正是存儲這些變量對象(包括父級的變量對象的列表),經過做用鏈域進行標示符的解析,函數的做用鏈域在函數調用時進行建立
如何進行標示符解析
var x = 10; function test() { var y = 20; console.log(x+y); } test();
上面的例子中在全局上下文中的變量對象中是存在變量X 函數test 在函數test的活動對象中存在着變量y 可是函數執行的過程當中能到查找到x,也就是在test的執行上下文中可以查找到x 這個機制是什麼呢? 就是經過[[scope]]屬性,這個屬性在函數建立的時候被建立,包含父級上下文的全部變量對象加上自身的變量對象(活動對象)構成做用鏈域(也就是存在當前的變量對象的變量或者函數聲明屏蔽父級變量對象的變量或者函數聲明),[[scope]]在函數建立建立的時候被存儲,它是靜態的屬性
var x = 10; function test(){ var x = 10; console.log(x); } test();
上面的例子中在test的做用鏈域中是有test的活動對象 + [[scope]]組成,當前test的上下文中[[scope]]屬性值只包含全局上下文的變量對象也就是全局對象,在標示符解析的過程當中優先從做用鏈域的頂端test的活動對象中查找,在經過[[scope]]屬相沿着父級的變量對象一級一級進行查找,知道找到相應的變量或者函數聲明,未找到返回undefined
例外 經過Function建立的函數的[[scope]]中只包含全局對象
正文
在執行鏈域中處於深層的標示符解析的時候,由於要經過做用鏈域進行查找,位置越深的讀取速度也就越慢,如下是提供標示符解析性能的一些方法
(1)緩存跨做用域的值
function test() { var doc = document; var getId = doc.getElementById("aa"); }
在函數中緩存全局變量的document屬性,在函數test中進行屢次使用的時候,就能減小變量查找的過程
(2)在經過原型鏈進行查找的過程當中,對象成員的層次越深,查找的性能越慢,屢次進行讀寫的話也就更加下降性能,因此涉及到對對象成員的查找的時候也進行必定的緩存
(3)避免使用with語句 with會在做用鏈域的開頭加入新的對象對象從而致使原來的變量對象處於做用鏈域的第二位置,下降了訪問的性能