前言
在网上看到一篇很好的思维导图的文章。之前自己学习总是毫无目的,东打一枪,西打一炮的。决定按照别人的思维导图整理知识点,以分支为单位,预计10篇文章左右来整理。
下面直接上图:

执行上下文
执行上下文
在理解这个执行上下文前,先来看一个问题——js执行顺序的问题。Javascript给我们一个很直观的感受,就是顺序执行。
function foo() {
console.log('foo1');
}
foo(); // foo2
function foo() {
console.log('foo2');
}
foo(); // foo2
由这个题,我们可以知道这是因为javascript引擎并非是一行一行的分析和执行程序,而是一段段的去执行。当执行一段代码的时候。比如函数提升的这个过程。由此引出两个问题:1.这个段是如何划分的。2.javascript引擎遇到怎么样的代码会进行准备工作。
- javascript的可执行代码有那些呢? 全局代码,函数代码,eval代码
当执行到一个函数的时候,便会进行一个准备工作。那么这个准备的过程,咱们就叫他执行上下文。
-
全局执行上下文 任何不在函数内部的代码都在全局上下文忠。它会执行两件事件:创建一个全局的window对象(浏览器的情况下)。并且设置this的值等于这个全局对象。一个程序忠只有一个全局执行上下文。
-
函数执行上下文 每当一个函数被调用的时候,都会为这个函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用的时候创建的。函数上下文可以有任意多个。
-
Eval函数执行的上下文
- 执行在 eval 函数内部的代码也会有它属于自己的执行上下文。eval方法就像是一个完整的ECMAScript解析器,它只接受一个参数,即需要执行的ECMAScript(或JavaScript)字符串。
- 缺点:1.性能差:引擎无法在编译时候对作用域查找进行优化。2.欺骗作用域。
接下来的问题在于,我们写的函数多了去了,怎么管理这么多的上下文呢?所以javaScript引擎创建了执行上下文栈。
- 执行栈(一种后进先出的数据结构) 当JavaScript引擎开始解释执行代码的时候: 首先遇到的就是全局解释执行的代码,直接将全局执行上下文压入执行栈,只有当整个应用程序结束时才被清空。所以执行栈的最底部永远有个全局执行上下文栈。函数调用:为函数创建一个新的执行上下文压入当前执行栈。后进先出。
下面咱们直接来看一段代码:
```javascript
var a = '0';
console.log(a);
function first() {
console.log('1');
second();
console.log('3');
}
function second() {
console.log('2');
}
first();
console.log('4');
//0 1 2 3 4
```
到这里先留下两个题,随后咱们就进入下一个话题作用域/作用域链。
问1执行结果?问2两种的上下文栈的具体变化有何不同?
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
作用域/作用域链
作用域:作用域是指程序源代码中定义变量的区域。作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
静态作用域:函数作用域在函数定义的时候就决定了。
动态作用域:函数作用域在函数调用的时候才决定的。
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
-
站在静态作用域上思考这个问题: 执行foo函数,foo函数内部没有value这个局部变量,没有就去找书写的位置,所以打印结果是1.
-
站在动态作用域上思考这个问题: 执行foo函数,foo函数内部查找有没有value这个局部变量,没有就去调用的作用域里面去找,结果为2.
-
打印结果:1,也就是说javascript是采用静态作用域。
-
问题 现在我们再回过头来再看看执行上下文中的两段代码。
打印结果都是:local scope.
原因: JavaScript采用词法作用域,函数的作用域基于函数创建的位置。
闭包
一个函数内嵌套一个函数。那么这个嵌套的函数就被称为闭包。(外部是代码块依旧产生闭包)
闭包的例子:
(function(){
let x=1;
function log(){
console.log(x);
}
log();
})
{
let x = 1;
setTimeout(function log(){
console.log(x);
}, 10000);
}
* 内部函数可以访问外部函数中定义的变量,即使是外部函数已经执行完毕。
(function autorun(){
let x = 1;
setTimeout(function log(){
console.log(x);
}, 10000);
})();
* 闭包和循环
咱们先来看一个经典的问题
function b(){
for(var i=1; i<=3; i++){
setTimeout(()=>{
console.log(i);
},1000);
}
}
b();
打印结果多少呢?444
闭包只存储外部变量的引用,而不会拷贝外部变量的值。
* 解决办法:IIFE和使用let变量声明
总结:
1. 闭包是一个可以访问外部作用域中变量的内部函数。
2. 被引用的变量只有在闭包被销毁的时候才会销毁。
3. 闭包使得定时器,事件处理,ajax请求等异步任务更加容易。
4. 可以通过闭包达到封装性
最后
* 以上都是基于网上的各类文章资料的整理,加上自己学习的记录。
* 下一章内容:this/apply/call/bind的那些事。