了解作用域和作用域链之前,先要了解“执行上下文”
执行上下文
原文:
https://juejin.cn/post/6954966248233009182
我以为JavaScript里代码的执行是按照顺序从上往下执行,那么下面的代码是 num在声明之前就已经赋值,应该报错
num = 3;
var num;
console.log(num);//输出 3
但并没有报错,这是因为 var num; 与 num = 3; 是属于代码运行的两个不同阶段的任务—— 编译阶段 与 执行阶段 。编译发生在执行之前,所以 var num; 先被执行,num = 3 便不被报错了。
声明语句(var num)最先被执行好比将声明语句 “移动” 至当前作用域的顶端,这个过程被称作提升。而提升与执行上下文的关系则十分密切(见后文)。
写不下去了,直接看原文吧!
。。。。。。。。。。…我…是…分…割…线…。。。。。。。。。。。
原文:
https://blog.csdn.net/qq_39903567/article/details/115069564
当JavaScript代码执行一段可执行代码(executable code)时,
会创建对应的执行上下文(execution context)。
对于每个执行上下文,都有三个重要属性:
变量对象(Variable object,VO)
作用域链(Scope chain)
this
作用域
作用域,就是变量或者是函数能作用的范围。
1.全局作用域
除了函数中定义的变量之外,都是全局作用域。
var a = 1;
function bar(){
console.log(a);
}
bar();//1
a就是全局变量,在函数里也可以访问a
2.函数作用域
创建函数后在花括号中声明的一些语句或变量只在当前函数中起作用。
var a = 10;
function bar(){
var a = 20;
console.log(a);
}
console.log(a);//10,取的全局作用域中的a
bar();//20,取的局部作用域中的a
3.块级作用域
ES6带来的新特性,在语句块中声明的语句或变量只在当前语句块中起作用。
函数作用域和块级作用域根本没有直接关系,函数作用域在ES5和ES6作用完全一样,变量不论是使用var声明还是使用了let ,const声明在外部都是不可以访问的。
块级作用域指的就是使用 if () { }; while ( ) { } …这些语句所形成的语句块 , 并且其中变量必须使用let或const声明(否则就不是块级作用域了),保证了外部不可以访问语句块中的变量。
if(true) {
let name='douqing';
console.log(name); // douqing
}
console.log(name); // ReferenceError: name is not defined
经典例子
for(var i=0;i<4;i++){
setTimeout(function(){
console.log(i);
},200);
}
//4 4 4 4
利用函数形成函数作用域
for(var i=0;i<4;i++){
(function(j){
setTimeout(function(){
console.log(j);
},200);
})(i)
}
//0 1 2 3
利用let形成块级作用域
for(let i=0;i<4;i++){
setTimeout(function(){
console.log(i);
},200);
}
//0 1 2 3
作用域链
当查找变量的时候,会先从当前作用域的变量对象中查找,如果没有找到,就会从父级作用域(上层环境)的变量对象中查找,一直找到全局作用域的变量对象,也就是全局对象。这样由多个作用域的变量对象构成的链表就叫做作用域链。它由当前环境与上层环境的一系列变量对象组成,保证了当前执行环境对符合访问权限的变量和函数的有序访问。
var a = 20;
function test() {
var b = a + 10;//当前作用域没有a,往上找,找到a = 20;
function innerTest() {
var c = 10;
return b + c;//当前作用域没有b,往上找,找到b = a+10;
}
console.log(d);//Uncaught ReferenceError: d is not defined,一直往上找,最终也没找到d,所以报错
return innerTest();
}
test();//40