执行上下文
每一个上下文都有一个相关联的变量对象(variable object),在这个上下文中定义的所以函数和变量都存在这个对象上。上下文再其所有代码执行完成后会被销毁,包括定义在上面的所有函数和变量(全局上下文在应用程序退出才会被销毁,比如关闭网页和浏览器)。执行上下文会在执行时候创建。
全局上下文
就是window对象,对全局数据进行预处理
var定义的全局变量==>undefined, 添加为window的属性
function声明的全局函数==>赋值(fun), 添加为window的方法
this==>赋值(window)
开始执行全局代码
函数上下文
在调用函数, 准备执行函数体之前, 创建对应的函数上下文对象(虚拟的, 存在于栈中)
对局部数据进行预处理
形参变量==>赋值(实参)==>添加为执行上下文的属性
arguments==>赋值(实参列表), 添加为执行上下文的属性
function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
var定义的局部变量==>undefined, 添加为执行上下文的属性
this==>赋值(调用函数的对象)
开始执行函数体代码
执行上下文栈
1. 在全局代码执行前, JS引擎(就是JS内核)就会创建一个栈来存储管理所有的执行上下文对象
2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
4. 在当前函数执行完后,将栈顶的对象移除(出栈)
5. 当所有的代码执行完后, 栈中只剩下window
console.log('gb: '+ i)
var i = 1
foo(1)
function foo(i) {
if (i == 4) {
return
}
console.log('fb:' + i)
foo(i + 1) //递归调用: 在函数内部调用自己
console.log('fe:' + i)
}
console.log('ge: ' + i)
两个问题:
1.依次输出什么?
gb: undefined
fb: 1
fb: 2
fb: 3
fe: 3
fe: 2
fe: 1
ge: 1
2. 整个过程中产生了几个执行上下文? 5个(递归函数执行一次就会产生一个)
下面再看一些例子:
1.先执行变量提升, 再执行函数提升?
function a() {}
var a
console.log(typeof a) // 'function'
函数提升的优先级大于变量提升
var c = 1
function c(c) {
console.log(c)
var c = 3
}
c(2) // 报错
为什么会报错?此时C并不是函数(Uncaught TypeError: c is not a function)
记住一句话:函数提升的优先级大于变量提升,且不会被同名变量声明所覆盖,但是同名变量赋值后会被覆盖
作用域
如何理解?其实就是代码段所管理的区域,它是静态的(相对于上下文对象,上下文是执行时候才会确定), 在编写代码时就确定了 。作用是隔离变量,不同作用域下同名变量不会有冲突。分为全局作用域和函数作用域 。
作用域和上下文的区别与联系:
1. 区别1
全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
全局上下文环境(这是一个对象)是在全局作用域确定之后, js代码马上执行之前创建
函数上下文是在调用函数时, 函数体代码执行之前创建
2. 区别2
作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放
3. 联系
上下文环境(对象)是从属于所在的作用域
全局上下文环境==>全局作用域
函数上下文环境==>对应的函数使用域
看一个例子:
var a = 10,
b = 20
function fn(x) {
var a = 100,
c = 300;
console.log('fn()', a, b, c, x)
function bar(x) {
var a = 1000,
d = 400
console.log('bar()', a, b, c, d, x)
}
bar(100)
bar(200)
}
fn(10)
作用域链
函数作用域链:函数先在自身找变量,找不到时,就会找函数的父函数,父函数中没有就找爷爷函数,以此类推,直到找到window全局作用域。如果都没有只能报错了。
看一个例子:
var a = 1
function fn1() {
var b = 2
function fn2() {
var c = 3
console.log(c)
console.log(b)
console.log(a)
console.log(d)
}
fn2()
}
fn1()
d找不到就会报错!!!下面再看一些例子:为什么报错呢?
因为在匿名函数作用域中没有fn2变量,会向外层作用域找(全局作用域),依然没有找到,就会报错。上面函数表达式,window对象上会有fn函数,所以执行fn时,在全局上下文中能找到fn。
目录
var fn = function () {
console.log(fn)
}
fn()
var obj = {
fn2: function () {
console.log(fn2)//报错,因为在函数体内没有fn2变量,如果要访问fn2属性,必须加this关键字
//console.log(this.fn2)
}
}
obj.fn2()
下面为什么输出是10?当函数定义好了,作用域就已经固定了,有三个作用域:全局作用域,fn函数作用域和show函数作用域。其实将fn函数作为参数传入就相当于执行fn函数,fn执行时会向外层作用域找是否有x变量。
var x = 10;
function fn() {
console.log(x);
}
function show(f) {
var x = 20;
f();
}
show(fn);//10