js 代码是如何执行的
执行上下文栈
变量对象(VO) 作用域链(scope chain) this(静态作用域)
执行上下文堆栈: 全局代码、函数代码、eval代码
每个代码是在其执行上下文中被取值的
有且只有一个全局上下文 可能有多个函数执行上下文以及eval执行上下文
执行的时候有执行上下文,不是下载的时候
一个函数可能会创建无数个上下文 函数每次调用都会生成一个具有新状态的上下文
当一个caller(触发其他上下文)触发了一个 callee(被触发的上下文),这个caller会暂缓自身的执行,然后把控制权传递给callee。这个callee被push到栈中,并成为一个运行中(活动的)执行上下文。在callee上下文结束后,它会将控制权返回给caller,然后caller的上下文继续执行(它可能触发其他上下文)直到它结束。以此类推,callee可能简单的返回或由于异常而退出,一个抛出但是没有被捕获的异常可能退出(从栈中pop)一个或者多个上下文。
栈中每个执行上下文都可以用一个对象表示
变量对象(与执行上下文相关的数据作用域)---(存储了在上下文中定义的变量和函数声明)
只有函数可以创建一个新的作用域
function bar(){} 声明
(function baz(){}); 表达式 不在变量对象之中
面试: 什么叫闭包?
在函数作用域中所定义的变量和内部函数在函数外边是不能直接访问到的,而且并不会污染全局上下文
闭包=函数+函数能够访问的自由变量
搞清楚 AO (activation object -- 活动对象) ? GO(global object)?VO(变量对象)
funciton fun3(){
}
function fun2(){
fun3()
}
function fun1(){
fun2()
}
fun1()
//fun1
ECStack.push(<fun1>functionContext)
//fun2
ECStack.push(<fun2>functionContext)
//fun3
ECStack.push(<fun3>functionContext)
//fun3执行完毕
ECStack.pop()
//fun2执行完毕
ECStack.pop()
//fun1执行完毕
ECStack.pop()
js继续执行其他代码,ECStack底层永远有一个globalContext
活动对象
当一个函数被caller触发(调用),一个特殊的对象,叫做活动对象
函数的变量对象也是一个同样简单的变量对象,但是除了变量和函数声明外,它还存储了形参和arguments对象,叫做活动对象
活动对象就是比平时的变量对象多了形参和arguments对象
函数表达式是不会包含在变量或者活动对象中
function foo(x,y){
var z = 30
function bar(){} //FD 函数的声明
(function baz(){}) //FE 函数的表达式
}
foo(10,20) 形参
Acitvation Object
x 10
y 20
arguments {x:10,y:20}
z
bar
变量和函数声明提升
alert(x) //funciton
var x = 10
alert(x) //10
funtion x(){}
alert(x) //20
##函数提升
在进入上下文的时候,变量对象会填充为函数声明。 变量对象填充是有顺序的。 (函数提升优先于变量提升)
(第一阶段)填充VO顺序 :函数的形参-》函数声明-》变量声明
当变量声明VO中遇到同名的函数名或函数参数名相同,不影响已存在的属性
处理上下文两个阶段
1: 进入执行上下文
2: 执行代码
funtion test(a,b){
var c = 10
function d(){}
var e = funtion _e(){}
(function x(){});
}
test(10)
AO(test) = {
a:10,
b:undefined,
c:undefined,
d:
e:undefined
}
执行代码
AO['c' ] = 10
AO['e'] = function _e(){}
if(!('a') in window){
var a = 1
}
alert(a) //undefined
// var a = 1 先变量声明,后赋值
// AO = {a:undefined}
// a in window => true 不会进入 a = 1的赋值语句
// alert(a) => undefined
var a = 1,
b = function a(x){
x && a(--x)
}
alert(a)
//进入执行上下文
// AO = {a: undefined, b: }
// 执行代码
// AO = {a:1, b:function a(x){} }
// alert(a) => 1
// b = function a(x){} 这里的function a是函数表达式,属于局部变量,不能在全局作用域中访问到
function a(x){
return x*2
}
var a
alert (a)
// 在执行上下文中,变量声明对象会被填充为函数声明
//函数形参-> 函数声明-> 变量声明
// AO = {fun a: }
// alert(a) => function a(x){return x*2}
function b (x, y, a) {
arguments[2] = 10
console.log(a)
}
b(1, 2, 3)
// 形参->函数声明->变量声明
// AO(b) = {x:1, y:2, a:3, arguments:{x:1, y:2, a:3}, arguments[2]:3 }
// arguments[2] = 10 或者 a = 10
// alert(a) => 10
// 使用arguments[index] 可以修改形参
function a () {
alert(this)
}
a.call (null)
// call第一个形参就是this所指向的对象
// AO(a) = {this: null||[object Window]}
// alert(this) => [object Window]
// call方法是借用别人的方法当做自己的方法,它可以接收多个参数。第一个参数就是自身的调用者,this。如果this为null的话,则指向全局对象window。