目录
一、变量提升与函数提升
1.变量声明提升
通过var定义(声明)的变量,在定义语句之前就可以访问到
值:undefined
2.函数声明提升
通过function声明的函数,在之前就可以直接调用。必须使用函数声明的方式
值:函数定义(对象)
3.问题:变量提升和函数提升是如何产生的?
1、在js中js引擎会优先解析var变量和function定义!在预解析完成后从上到下逐步进行!
2、解析var变量时,会把值存储在“执行环境”中,而不会去赋值,值是存储作用!例如:
alert(a); var a = 2; 这时会输出undifiend,意思是没有被初始化没有被赋值!这并不是没有被定义,错误了的意思!
3、在解析function时会把函数整体定义,这也就解释了为什么在function定义函数时为什么可以先调用后声明了!其实表面上看是先调用了,其实在内部机制中第一步实行的是把以function方式定义的函数先声明了(预处理)
console.log(b); //undefined 变量提升
fn2(); //“fn2()”可调用 函数提升
fn3(); //不能 变量提升
var b=3;
function fn2(){
console.log("fn2()");
}
var fn3 = function(){
console.log("fn3()");
}
二、执行上下文
1.代码分类
*全局代码
*函数(局部)代码
2.全局执行上下文
*在执行全局代码前将window确定为全局执行上下文
*对全局数据进行预处理
*var定义的全局变量==>undefined,添加为window属性
*function声明的全局函数 ==> 赋值(fun),添加为window的方法
*this==>赋值(window)
*开始执行全局代码
//全局执行上下文
console.log(a1,window.a1); //undefined undefined
a2() //"a2()"
console.log(this); //Window
var a1 = 3;
function a2(){
console.log("a2()");
}
3.函数执行上下文
*在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
*对局部数据进行预处理
*形参变量==>赋值(实参)==>添加为执行上下文的属性
arguments==>赋值(实参列表),添加为执行上下文的属性
var定义的局部变量==>undefined,添加为执行上下文的属性
function声明的函数 ==> 赋值(fun),添加为执行上下文的方法
this ==> 赋值(调用函数的对象)
开始执行函数体代码
//函数执行上下文
function fn(a1){
console.log(a1); //5
console.log(a2); //undefined
a3() //"a3()"
console.log(this); //window
console.log(arguments); //伪数组(5,6)
var a2 = 3;
function a3(){
console.log("a3()");
}
}
fn(5,6)
三、执行上下文栈
1.在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
3.在函数执行上下文创建后,将其添加到栈中(压栈)
4.当前函数执行完后,将栈顶的对象移除(出栈)
5.当所有的代码执行完后,栈中只剩下window
注意:
在栈中,全局执行上下文只有一个(window),剩下的都是函数执行上下文
先执行变量提升,再执行函数提升
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
四、作用域
1.理解
就是一块“地盘”,一个代码段所在的区域
它是静态的(相对于上下文对象),在编写代码时就确定了
2.分类
全局作用域
函数作用域
没有块作用域(ES6有了)
3.作用
隔离变量,不同作用域下同名变量不会有冲突
JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。
ECMAScript 6(简称ES6)中新增了块级作用域。
块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域,if语句和for语句中用var定义的变量可以在外面访问到。
通过var定义的变量可以跨块作用域访问到,不能跨函数作用域访问到。
var、let、const的区别
var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
五、作用域与执行上下文
1.区别1
*全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时。
*全局执行上下文环境是在全局作用域确定之后,JS代码马上执行之前创建
*函数执行上下文是在调用函数时,函数体代码执行之前创建
2.区别2
*作用域是静态的,只要函数定义好了就一直存在,且不会再变化
*执行上下文是动态的,调用函数时创建,函数调用结束时上下文环境就会自动释放
3.联系
*执行上下文(对象)是从属于所在的作用域
*全局上下文环境从属于全局作用域
*函数上下文环境从属于对应的函数作用域
六、作用域链
1.理解
*多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外)
*查找多个变量时就是沿着作用域链来查找的
2.查找一个变量的查找规则
1.在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
2.在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3
3.再执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的异常