执行上下文
上下文的概念在JavaScript中很重要。变量和函数的上下文决定了它们可以访问哪些数据,以及它们的行为。每个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存在于这个对象上。
全局上下文是最外层的上下文。在浏览器中,全局上下文就是我们常说的window对象。所有通过var定义的全局变量和函数都会成为window对象的属性和方法。
上下文在其所有代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数(全局上下文在应用程序退出前才会被销毁,比如关闭页面或退出浏览器)。
作用域链
执行上下文是在调用执行函数的时候产生的,而作用域是静态的,在编写代码时就产生了。
上下文的代码在执行的时候,会创建变量对象的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于作用域链的最前端。全局上下文的变量对象始终是作用域链的最后一个变量对象。
代码执行的标识符解析是通过沿作用域链逐级搜索标识符名称完成的。搜索的过程始终从作用域链的最前端开始,然后逐级往后,直到找到标识符。
var color = "blue"
function changeColor() {
let anotherColor = "red"
function swapColors() {
let tempColor = anotherColor
anotherColor = color
color = tempColor
//这里可以访问color、anotherColors、tempColor
}
//这里可以访问color和anotherColors,但访问不到tempColor
swapColors()
}
//这里只能访问color
changeColor()
以上代码涉及3个上下文:全局上下文、changeColor()的局部上下文和swapColor()的局部上下文。
内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文中的任何东西。
举些栗子
function foo() {
console.log(a);
a = 1;
}
foo(); // ???
function bar() {
a = 1;
console.log(a);
}
bar(); // ???
第一段会报错,第二段会打印1。
因为第一段执行 console 的时候,没有 a 的值,然后就会到全局去找,全局也没有,所以会报错。当第二段执行 console 的时候,全局对象已经被赋予了 a 属性,这时候就可以从全局找到 a 的值,所以会打印 1。
console.log(foo);
function foo(){
console.log("foo");
}
var foo = 1;
会打印函数,而不是 undefined 。
这是因为在进入执行上下文时,首先会处理函数声明,其次会处理变量声明,如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。
var foo = 1
function bar(foo){
console.log(foo)
function foo(){}
var foo = 2
}
bar(3)
会打印foo函数,这是上一题的扩展。
因为在有参数、变量、函数的情况下,会首先处理函数声明,再是参数,再处理变量声明。
最后可以思考下这道题(面试中碰到的):
inner = 'window'
function say() {
console.log(inner)
console.log(this.inner)
}
var obj = (function() {
var inner = '1-1'
return {
inner: '1-2',
say: function() {
console.log(inner)
console.log(this.inner)
}
}
})()
say()
obj.say()
obj.say = say
obj.say()
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。