变量的分类
全局变量、局部变量。
script标签内顶层定义的变量是全局变量,函数内(作用域内)定义的变量叫局部变量。
作用域
scope作用域是一个虚拟的概念。直观来说,作用域就是变量(常量)起作用的区域,数据可访问的区域。
JavaScript采用词法作用域(lexical scoping),也就是静态作用域,在程序写成的时刻,作用域就已经确定了。
作用域分类
es5阶段使用var来定义变量,es6+使用let、const来定义。
es5的处理机制中,作用域分为全局作用域和函数作用域。也就是说,var定义的变量只在函数内是局部变量,不在函数内定义就都是全局变量。
es6的处理机制中,作用域分为全局作用域和块级作用域。函数也是属于块级作用域,简单来说,只要有花括号{}就会形成作用域。
在es6中,条件语句、循环语句等等都会形成作用域。
if (true) {
let a = 123;
}
for(let b=0;b<10;b++){
}
{
let c = 10;
}
以上代码中的变量a、b、c都是局部变量。
作用域链
作用域可以形成树形结构,每个作用域都可以与上级作用域关联,直到全局作用域。由当前作用域逐级向全局作用域关联,形成的链条就叫做,作用域链。
我们访问变量时,先在当前作用域中查找,如果没有找到,则沿着作用域链向上级逐级查找,直到全局为止。
var x = 1;
fn();
function fn() {
alert(x);//undefined
x++;
alert(x);//NaN
var x = 456;
alert(x);//456
}
当我们需要访问变量x时,首先在当前作用域内找局部变量x,如果找到了,则不会继续访问上层作用域。上例中,全局变量x不会影响到函数fn内,因为,fn内有局部变量x。
执行上下文
作用域是静态的,在代码写成时就已经确定了。
而执行上下文是代码在执行时才会创建的。
当程序被执行时:
1、首先生成全局执行上下文,并插入内存,且自动开始执行。
2、当执行到函数时,会再次生成函数的执行上下文(局部执行上下文),插入到调用函数的地方。
3、全局执行上下文全部执行完成,程序才算执行完成。
执行上下文的分类
执行上下文分为:全局执行上下文、局部执行上下文、eval方法形成的上下文(eval函数的作用是将字符串形式的js脚本执行)。
全局执行上下文在程序开始执行时就会生成,它是惟一的。
局部执行上下文,所有可以形成独立作用域的代码都会生成局部执行上下文。
eval方法形成的执行上下文也算作局部上下文,隶属于全局执行上下文的。
执行上下文的生命周期
执行上下文的生命周期包括三个阶段:创建阶段 → 执行阶段 → 回收阶段
创建阶段 :
-
确定变量:
如果是函数则先确定参数且赋值。其次确定变量(函数),不赋值。
-
创建作用域链:
根据作用域链,将链条上的执行上下文的内存地址确定并存储。
为什么要存储执行上下文的内存地址,因为程序只有执行了(生成了执行上下文)才能确定变量的值。
-
确定this指向:
this是一个虚拟的指针,存储的是执行上下文的内存地址。
执行阶段:
按顺序依次执行代码。(如果有函数或其他会生成独立作用域的语句执行,则会继续生成局部执行上下文)。
回收阶段:
等待js引擎的自动垃圾回收机制回收资源。
作用域与执行上下文的不同
作用域是静态的,程序写成的时候,作用域就可以确定了。
执行上下文是动态的,程序被调用时才会生成执行上下文。
例如:函数在写成时,作用域就确定了,被调用时才会生成执行上下文,如果被多次调用,会生成多个执行上下文。