变量提升+函数提升+执行上下文
210310
一、变量声明提升
- 通过var定义(声明)的变量,在定义语句之前就可以访问到
- 值:undefined
二、函数声明提升
- 通过function声明的函数,在之前就可以直接调用
- 值:函数定义(对象)
var a = 3
function fn(){
/*相当于先声明了a var a;a = undefined
先在自身函数中找变量,再去全局找
*/
console.log(a) //undefined
var a = 4
}
fn() //undefined
console.log(b) //undefined //变量提升
fn2() //可调用 函数提升
fn3() //变量提升,不可执行
var b = 3
function fn2(){
console.log('fn2')
}
var fn3 = function(){
console.log('fn3')
}
注意:
- 函数声明优先级最高
function fn(){} - 其次:函数表达式和变量,按顺序执行
①匿名函数表达式:fn=function(){}
②具名函数表达式:fn=function bar(){}
③变量:var a
测试一:
function a(){}
var a
console.log(typeof a) //function
function a(){}
var a = 1
console.log(typeof a) //number
测试二:
var c = 1 //=>①var c
function c(c){
console.log(c)
var c = 3 //无关
}
//=>②c = 1 =>c typeof number
c(2) //报错c is not a function
三、执行上下文
-
代码分类(位置)
- 全局代码
- 函数(局部)代码
-
全局执行上下文
-
在执行全局代码前将window确定为全局执行上下文
-
对全局数据进行预处理(收集数据)
- var定义的全局变量==>(初始化赋值)undefined,添加为window的属性
- function声明的全局函数==>赋值(函数对象),添加为window的方法
- this==>赋值(window)
-
开始执行全局代码
c = 3 //特殊:未执行语句之前,window中不包含此变量,需用var定义 console.log(a1,window.a1) //undefined a2() //a2() console.log(this) //window var a1 = 2 function a2(){ console.log('a2()') } console.log(a1) //2
-
-
函数执行上下文
-
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
-
对局部数据进行预处理
- 形参变量>赋值(实参)>添加为执行上下文的属性
- arguments==>赋值(实参列表),添加为执行上下文的属性
- var定义的局部变量==>undefined,添加为执行上下文的属性
- function声明的函数==>赋值(函数对象),添加为执行上下文的方法
- this==>赋值(调用函数的对象)
-
开始执行函数体代码
function f1(a1){ console.log(a1) console.log(a2) a3() console.log(this) console.log(arguments) var a2 = 3 function a3(){ console.log('a3()') } } f1(2,3) //2,undefined,a3(),window,2 3
-
210314执行上下文栈
- 在全局代码运行前,js引擎就会创建一个栈来存储管理所有的执行上下文对象
- 在全局执行上下文(window)确定后,将其添加到栈中(压栈)
- 在函数执行上下文创建后,将其添加到栈中(压栈)
- 在当前函数执行完后,将栈顶的对象移出(出栈)
- 当所有的代码执行完毕,栈中只剩下window
//调用一次函数产生一次执行上下文栈n+1(window)
//1、进入window全局执行上下文
var a = 10
var bar = function(x){
var b = 5
//2、进入foo执行上下文
foo(x + b)
}
var foo = function(y){
var c = 5
console.log(a + c + y)
}
//3、进入bar函数执行上下文
bar(10) //30
console.log('global begin:' + i)
var i = 1
foo(1)
function foo(i){
if(i == 4){
return
}
console.log('foo() begin:' + i)
foo(i + 1)//递归调用:在函数内部调用自己
console.log('foo() end:' + i)
}
console.log('global end:' + i)
/*函数依次输出:
global begin:undefined
foo() begin:1
foo() begin:2
foo() begin:3
foo() end:3
foo() end:2
foo() end:1
global end:1
整个过程中产生了五个执行上下文
*/
210315执行上下文的作用域n(定义的函数)+1(全局)
有n(调用函数的次数)+1(window)执行上下文
-
理解
- 就是一块“地盘”,一个代码段所在的地区
- 是静态的(相对于上下文对象),在编写代码时就确认了
-
分类
- 全局作用域
- 函数作用域
- 没有块作用域(ES6有)
if(true){ var c = 3 } console.log(c) //3 全局中有c变量,没有块作用域
-
作用
- 隔离变量,不同作用域下同名变量不会有冲突