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') }) }) 复制代码