js基础知识(4)-执行上下文

当执行 JS 代码时,会产生三种执行上下文
• 全局执行上下文
• 函数执行上下文
• eval 执行上下文

当浏览器首次载入你的脚本,它将默认进入全局执行上下文。如果,你在你的全局代码中调用一个函数,你程序的时序将进入被调用的函数,并创建一个新的执行上下文,并将新创建的上下文压入执行栈的顶部。
如果你调用当前函数内部的其他函数,相同的事情会在此上演。代码的执行流程进入内部函数,创建一个新的执行上下文并把它压入执行栈的顶部。浏览器总会执行位于栈顶的执行上下文,一旦当前上下文函数执行结束,它将被从栈顶弹出,并将上下文控制权交给当前的栈。这样,堆栈中的上下文就会被依次执行并且弹出堆栈,直到回到全局的上下文。

每个执行上下文中都有三个重要的属性 

(1)VO(变量对象)或者AO(活动对象),包含变量、函数声明和函数的形参(AOVO只能有一个)
(2)作用域链(scope)(JS 采用词法作用域,也就是说变量的作用域是在定义时就决定了)
(3)this

VO(变量对象)/AO(活动对象)

变量对象(Variable object)是说JS的执行上下文中都有个对象用来存放执行上下文中可被访问但是不能被delete的函数标示符、形参、变量声明等。它们会被挂在这个对象上,对象的属性对应它们的名字对象属性的值对应它们的值但这个对象是规范上或者说是引擎实现上的不可在JS环境中访问到活动对象

激活对象(Activation object):有了变量对象存每个上下文中的东西,但是它什么时候能被访问到呢?就是每进入一个执行上下文时,这个执行上下文中的变量对象就被激活,也就是该上下文中的函数标示符、形参、变量声明等就可以被访问到了

var a = 10
function foo(i) {
var b = 20
}
foo()

对于上述代码,执行栈中有两个上下文:全局上下文和函数 foo 上下文。
 

stack = [
globalContext,
fooContext
]

对于全局上下文来说,VO 大概是这样的
 

globalContext.VO === globe
globalContext.VO = {
a: undefned,
foo: <Function>,
}

对于函数 foo 来说,VO 不能访问,只能访问到活动对象(AO)
 

fooContext.VO === foo.AO
fooContext.AO {
i: undefned,
b: undefned,
arguments: <>
}
// arguments 是函数独有的对象(箭头函数没有)
// 该对象是一个伪数组,有 `length` 属性且可以通过下标访问元素
// 该对象中的 `callee` 属性代表函数本身
// `caller` 属性代表函数的调用者

对于作用域链,可以把它理解成包含自身变量对象和上级变量对象的列表,通过[[Scope]] 属性查找上级变量
 

扫描二维码关注公众号,回复: 4191010 查看本文章
fooContext.[[Scope]] = [
globalContext.VO
]
fooContext.Scope = fooContext.[[Scope]] + fooContext.VO
fooContext.Scope = [
fooContext.VO,
globalContext.VO
]

接下来让我们看一个老生常谈的例子,var
 

b() // call b
console.log(a) // undefned
var a = 'Hello world'
function b() {
console.log('call b')
}

想必以上的输出大家肯定都已经明白了,这是因为函数和变量提升的原因。通常提升的解释是说将声明的代码移动到了顶部,这其实没有什么错误,便于大家理解。但是更准确的解释应该是:在生成执行上下文时,会有两个阶段。第一个阶段是创建的阶段(具体步骤是创建 VO),JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为undefined,所以在第二个阶段,也就是代码执行阶段,我们可以直接提前使用。在提升的过程中,相同的函数会覆盖上一个函数,并且函数优先于变量提升
 

猜你喜欢

转载自blog.csdn.net/cao_dan/article/details/81667177
今日推荐