全局作用域
- 全局作用域在页面打开时创建,页面关闭时销毁
- 全局作用域的顶级对象是window,由浏览器创建
- 全局作用域中声明的变量、函数都是window的对象
函数作用域
- 函数作用域在函数被调用时创建,在函数执行完毕后销毁
- 每次调用函数,都会创建一个新的作用域,它们之间是相互独立的
- 全局作用域的变量可以在函数作用域中访问,反之不行
- 在函数作用域中访问变量时,如果作用域内有,访问,如果没有,就去上一层作用域访问,一直到全局作用域,还是没有,就是未定义undefined
深入理解作用域
执行期的上下文
- 当函数代码执行的前期,会创建一个执行期上下文内部对象AO
- 这个内部对象是预编译的时候创建的,函数被调用时会先进行预编译
- 全局代码执行的前期也会创建一个执行期的上下文对象GO
函数作用域的预编译
- 首先,创建AO对象 AO{}
- 接着,找形参和变量声明,将变量和形参名当作AO对象的属性名,值为undefined
- 实参形参相统一
- 在函数体里找函数声明,值赋为函数体
全局作用域的预编译
- 创建GO对象
- 找变量声明,将变量名作为GO对象的属性名,值为undefined
- 找函数声明,值赋为函数体
function fn(a,c){
console.log(a)//function a(){}
var a=123
console.log(a)//123
console.log(c)//function c(){}
function a(){}
if(false){
var d=678
}
console.log(d)//undefined
console.log(b)//undefined
var b=function(){}
console.log(b)//function(){}
function c(){}
console.log(c)//function c(){}
}
fn(1,2)
分析一下上述代码:
预编译阶段:
- 函数体中的形参为a,c,变量有a,b,c,d。这几个的值一开始都是undefined—将变量和形参名当作AO对象的属性名,值为undefined
- 函数调用,a=1,c=2—实参形参相统一
- a再从1改为function a(){},c从2改为function c(){}—找函数声明,值赋为函数体
到此,预编译结束
继续分析:
- 调用函数时,函数体内的第一句是打印a,此时a的值为function a(){}
- 第二句var a=123,console.log(a)//123
- console.log©,与第一句情况一样,此时c值为function c(){}
- function a(){} a值又变成了函数体
- if(false)虽然if条件为假,并不执行,但是预编译时,仍然会对d进行声明,只是不会赋值,所以后边console.log(d)//undefined
- 接着 console.log(b),由于js存在声明提前,所以先有var b,然后打印是undefined,最后再给b赋值为函数体(var b=function(){}这句不是函数声明,而是定义b)
- 最后打印c,c值仍为函数体
再来看一段代码:
var global
function fa() {
function fb() {
var bb = 123
aa = 0
console.log(bb)//123
console.log(aa)//0
}
console.log(aa)//undefined
var aa = 234
console.log(aa)//234
fb()
console.log(aa)//0
console.log(global)//undefined
}
fa()
再来理解一下,函数作用域是在函数被调用的时候创建的,fb函数也是在fa函数调用的时候才被定义的。
另外这里存在闭包的概念,aa = 0这句并不是全局定义,fb能够访问fa中的变量,而fa是有对aa的声明的。
总结:先分析预编译阶段,之后分析执行阶段,注意声明提前,只提前了变量和函数的声明,赋值不提前。