JavaScript中的運行環境大概包括三種狀況:html
每次當控制器轉到可執行代碼的時候,就會進入一個執行上下文。執行上下文能夠理解爲當前代碼的執行環境,它會造成一個做用域。前端
所以在一個JavaScript程序中,一定會產生多個執行上下文,JavaScript引擎會以函數調用棧的方式來處理它們。棧底永遠都是全局上下文,而棧頂就是當前正在執行的上下文。html5
var color = 'blue'; function changeColor() { var anotherColor = 'red'; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; } swapColors(); } changeColor(); 複製代碼
注意:函數中,遇到 return 能直接終止可執行代碼的執行,所以會直接將當前上下文彈出棧。git
全局上下文的生命週期,與程序的生命週期一致,只要程序運行不結束,好比關掉瀏覽器窗口,全局上下文就會一直存在。其餘全部的上下文環境,都能直接訪問全局上下文的屬性。github
解了這個過程以後,咱們就能夠對執行上下文作一些總結:面試
一個執行上下文的生命週期能夠分爲兩個階段:數組
建立階段:在這個階段中,執行上下文會分別建立變量對象,創建做用域鏈,以及肯定this的指向。promise
代碼執行階段:建立完成以後,就會開始執行代碼,這個時候,會完成變量賦值,函數引用,以及執行其餘代碼。瀏覽器
建立變量對象:安全
例子1:
function test() { console.log(a); console.log(foo()); var a = 1; function foo() { return 2; } } test(); 複製代碼
等價於
function test() { function foo() { return 2; } var a; console.log(a); console.log(foo()); a = 1; } test(); 複製代碼
例子2:
function test() { console.log(foo); console.log(bar); var foo = 'Hello'; console.log(foo); var bar = function () { return 'world'; } function foo() { return 'hello'; } } test(); 複製代碼
等價於
function test() { function foo() { return 'hello'; } var bar; console.log(foo); console.log(bar); foo = 'Hello'; console.log(foo); var bar = function () { return 'world'; } } test(); 複製代碼
未進入執行階段以前,變量對象中的屬性都不能訪問。可是進入執行階段以後,變量對象轉變爲了活動對象,裏面的屬性都能被訪問了,而後開始進行執行階段的操做。 變量對象和活動對象其實都是同一個對象,只是處於執行上下文的不一樣生命週期。不過只有處於函數調用棧棧頂的執行上下文中的變量對象,纔會變成活動對象。 咱們能夠用建立變量對象來理解變量提高。
創建做用域鏈: 做用域鏈,是由當前環境與上層環境的一系列變量對象組成,它保證了當前執行環境對符合訪問權限的變量和函數的有序訪問。
var a = 20; function test() { var b = a + 10; function innerTest() { var c = 10; return b + c; } return innerTest(); } test(); 複製代碼
肯定this的指向: this的指向,是在函數被調用的時候肯定的,在函數執行過程當中,this一旦被肯定,就不可更改了。若是調用者函數,被某一個對象所擁有,那麼該函數在調用時,內部的this指向該對象。若是函數獨立調用,那麼該函數內部的this,則指向undefined。
// demo01 var a = 20; function fn() { console.log(this.a); } fn(); 複製代碼
// demo02 var a = 20; function fn() { function foo() { console.log(this.a); } foo(); } fn(); 複製代碼
// demo03 var a = 20; var obj = { a: 10, c: this.a + 20, fn: function () { return this.a; } } console.log(obj.c); console.log(obj.fn()); 複製代碼
使用call,apply顯示指定this
function fn() { console.log(this.a); } var obj = { a: 20 } fn.call(obj); 複製代碼
call與applay後面的參數,都是向將要執行的函數傳遞參數。其中call以一個一個的形式傳遞,apply以數組的形式傳遞。這是他們惟一的不一樣。
function fn(num1, num2) { console.log(this.a + num1 + num2); } var obj = { a: 20 } fn.call(obj, 100, 10); fn.apply(obj, [20, 10]); 複製代碼
JS 引擎創建在單線程事件循環的概念上。單線程( Single-threaded )意味着同一時刻只能執行一段代碼,與 Swift、 Java 或 C++ 這種容許同時執行多段不一樣代碼的多線程語言造成了反差。
JavaScript代碼的執行過程當中,除了依靠函數調用棧來搞定函數的執行順序外,還依靠任務隊列(task queue)來搞定另一些代碼的執行。
例子1:
setTimeout(function() { console.log('timeout1'); }) new Promise(function(resolve) { console.log('promise1'); for(var i = 0; i < 1000; i++) { i == 99 && resolve(); } console.log('promise2'); }).then(function() { console.log('then1'); }) console.log('global1'); 複製代碼
例子2:
// demo02 console.log('glob1'); setTimeout(function() { console.log('timeout1'); process.nextTick(function() { console.log('timeout1_nextTick'); }) new Promise(function(resolve) { console.log('timeout1_promise'); resolve(); }).then(function() { console.log('timeout1_then') }) }) setImmediate(function() { console.log('immediate1'); process.nextTick(function() { console.log('immediate1_nextTick'); }) new Promise(function(resolve) { console.log('immediate1_promise'); resolve(); }).then(function() { console.log('immediate1_then') }) }) process.nextTick(function() { console.log('glob1_nextTick'); }) new Promise(function(resolve) { console.log('glob1_promise'); resolve(); }).then(function() { console.log('glob1_then') }) setTimeout(function() { console.log('timeout2'); process.nextTick(function() { console.log('timeout2_nextTick'); }) new Promise(function(resolve) { console.log('timeout2_promise'); resolve(); }).then(function() { console.log('timeout2_then') }) }) process.nextTick(function() { console.log('glob2_nextTick'); }) new Promise(function(resolve) { console.log('glob2_promise'); resolve(); }).then(function() { console.log('glob2_then') }) setImmediate(function() { console.log('immediate2'); process.nextTick(function() { console.log('immediate2_nextTick'); }) new Promise(function(resolve) { console.log('immediate2_promise'); resolve(); }).then(function() { console.log('immediate2_then') }) }) 複製代碼