前言:上文我们讲了js的执行上下文-作用域链,如果还有童鞋对执行上下文还没搞懂的话,建议看上篇文章 这有传送门:JavaScript深入之执行上下文
话不多说,我们直接看题目:
面试题一:
var n = 100
function foo() {
n = 200
}
foo()
console.log(n)
首先,简单描述一下(伪代码描述),解析全局代码的时候GO对象里有
这里写伪代码:
var globle object={
n:undefined,
foo:0xa00
}
开始执行第一行代码 var n=100;将GO对象的n变量赋值给100
伪代码:
var globle object={
n:100,
foo:0xa00
}
AO对象包括函数的形参、定义的变量、以及函数的声明
后面调用foo(),会先解析函数,先创建FEC执行上下文,其中包括VO对象,当FEC进入调用栈时,VO被激活成了AO会创建FEC,在FEC进入ECS时,VO对象被激活成AO对象,
很明显foo函数体这些都没有,foo的AO对象是空的
伪代码:
var foo AO={}
最终找到父级的VO也就是GO对象里面的n变量,修改赋值为200
伪代码:
var globle object={
n:200,
foo:0xa00
}
执行完毕,foo的FEC被弹出栈
执行最后一行代码 console.log(n),找到GO里面的n,最终打印为200
你做对了吗?
面试题二:
function foo() {
console.log(n)
var n = 200
console.log(n)
}
var n = 100
foo()
首先还是老样子,解析全局代码,创建GO对象
这里写伪代码:
var globle object={
foo:0xa00,
n:undefined
}
开始执行代码,先执行var n = 100,GO对象的里面的n变量被赋值为100
伪代码:
var globle object={
foo:0xa00,
n:100
}
调用foo()函数,会先解析函数,先创建FEC执行上下文,其中包括VO对象,当FEC进入调用栈时,VO被激活成了AO,AO伪代码:
伪代码:
var foo AO={
n:undefined
}
访问一个变量的时候,会沿着作用域链一层一层往上找,最后没有找到则会报错
开始执行foo函数的第一行代码 console.log(n),根据作用域链的查找规则,会先在自己的AO对象中找,foo的AO对象n的值为undefined,所以打印为undefined
开始执行foo函数的第二行代码: var n = 200,将自己AO对象的n的值赋值为200
开始执行foo函数的第二行代码: console.log(n),此时,foo的AO对象里面的n变量已经被赋值为200了,所以这行打印200,执行完毕,foo的FEC被弹出栈
你做对了吗?
面试题三:
var a = 100
function foo() {
console.log(a)
return
var a = 200
}
foo()
还是老样子,解析全局代码,创建GO对象
这里写伪代码:
var globle object={
a:undefined,
foo:0xa00
}
开始执行全局代码,第一行 var a = 100,将GO对象中的a变量赋值给100
伪代码:
var globle object={
a:100,
foo:0xa00
}
继续执行全局代码,调用foo()函数,会先解析函数,先创建FEC执行上下文,其中包括VO对象,当FEC进入调用栈时,VO被激活成了AO
伪代码:
var foo AO={
a:undefined
}
foo函数的AO对象为啥有n变量呢,这里是不是有同学有疑问呢?其实我们不要被return给干扰,当代码解析的时候,他是不会执行里面的东西,所以有return也没关系的(return是在代码执行的时候会执行return),不影响AO对象里面的内容(VO在解析阶段生成),FEC进入调用栈会先解析函数里面的代码
执行foo函数第一行代码:console.log(a),找到foo的AO对象,a为undefined,打印undefined
执行foo函数第一行代码:return,foo函数执行结束
你做对了吗?
面试题四:
function foo() {
var m = 100
}
foo()
console.log(m)
老规矩,解析全局代码,创建GO对象,在这里的GO对象中,我们自己定义的变量是空的(go对象有一些是默认的变量,Array,date等等,这里都没有写出来)
伪代码:
var globle object={
}
接着调用foo()函数,会先解析函数,先创建FEC执行上下文,其中包括VO对象,当FEC进入调用栈时,VO被激活成了AO
伪代码:
var foo AO={
m:undefined
}
执行foo函数,执行第一行代码var m = 100
执行foo函数完毕,foo的FEC从执行栈中弹出
开始执行最后一行代码console.log(m),此时根据作用域链的查找规则,会先在GO里面找,由于是全局代码没有父级作用域,GO里面没找到,最终代码报错ReferenceError: m is not defined
你做对了吗?
面试题四-2:
我们把上个面试题改一下,把foo函数里面的var声明去掉
function foo() {
m = 100
}
foo()
console.log(m)
其实严格来讲,这样写会报错,但是由于js边边角角的东西,确实可以这样写,当我们没有去声明或定义一个变量而是直接给他赋值时,会默认把这个变量提到全局。
所有很明显咯,m被提到了全局,所以最后一行打印为100
面试题五:
function foo() {
var a = b = 10
}
foo()
console.log(b)
其实在面对 var a = b = 10这种写法时,js引擎会转化为 var a=10; b=10
经过前面4道详细详解,那么咱们这道就简单讲一哈。
function foo() {
var a = b = 10
// => 转成下面的两行代码
// var a = 10
// b = 10
}
foo()
console.log(b)
先执行全局代码foo(),此时foo的AO对象中a的值为10,由于b没有被声明或者定义,所有b会被添加到全局对象,所以最终打印为10
面试题五-2:
function foo() {
var a = b = 10
}
foo()
console.log(a)
console.log(b)
如何第五道面试题改改呢,再打印一个a变量呢,其实结果很显然啦,全局对象并无a变量,根据作用域的查找规则,其实代码会报错啦ReferenceError: a is not defined
好啦,码字不易,经过前几道的作用域细讲,小伙伴们有没有彻底搞懂作用域面试呢?