作用域直白的意思的作用范围。
块级作用域:{
与}
之间的称为块级作用域。在Java与C语言中有块级作用域,而JavaScript中没有块级作用域。
词法作用域
JavaScript中采用的是词法作用域。在代码编写过程中,不需要运行,变量的作用域就已经确定。
动态作用域:变量的作用域取决于当时的执行环境。
词法作用域的规则
词法作用域中的访问原则——就近原则
首先在当前作用域中查找,如果没有就去上一级的作用域中查找,直到全局作用域,如果都没有返回undefined
或者报错(函数)。
注意:外层作用域不能访问内层作用域。只有函数才能限定作用域范围,函数内允许访问函数外的数据。
123456var a = 10;function fun() {console.log(a); //10var b = 1;}console.log(b); //undefined变量提升
变量提升
变量提升时只会提升到当前作用域的顶端。
JavaScript的解析过程
第一阶段:预处理(预解析)
现将代码读取到内存中检查,会将所有的声明在此时进行标记。所谓的标记就是让JS解释器知道有这个名字,后面在使用名字的时候,不会出现未定义的错误。这个标记的过程就是提升。
- 用
var
定义的变量 - 用声明方式创建的函数
在函数表达式中,只会提升var声明的变量,并不会把整个函数表达式提升
第二阶段:执行阶段
覆盖问题
- 函数名和函数名一样时,后面的会覆盖前面的
- 变量名和变量名一样时,后面的会覆盖前面的
- 函数名和变量名一样时,优先读取函数。即可看成只提升函数的声明,不提升同名的变量声明。
作用域链
- 函数可以创建限定的作用域
- 函数内可以声明一个函数
- 函数中的函数也可以创建函数
所以,形成一条向上的作用域链
最里层的函数可以逐级向上读取作用域
- 外层的作用域读取不到里层的作用域
综合笔试题
|
|
思考过程:
- Foo.getName();
首先先声明了一个构造函数,其中有getName
这个属性,但是随后对该属性进行了重新赋值,覆盖了原来的属性。所以,此时执行覆盖后的函数,输出2
。 - getName();
getName()
只需要看最后两个函数。首先函数表达式进行变量提升,将var getName
提升到顶端,但是函数主体依然不动。最后一个函数声明直接提升到var getName
的后面,但是还是在第四个函数的前面。所以最后输出最后面的函数,就是4
。 - Foo().getName();
先执行Foo()
,得到Foo
返回值对象的getName()
。在执行Foo()
时,返回值的this
指向window
。getName = function(){console.log("1");};
是一个函数赋值语句,是全局变量,先向当前Foo
函数作用域内寻找getName
变量,没有找到,在全局作用域中查找,找到了全局中getName
的console.log(4)
语句,将此变量的值赋值为function(){console.log("1");};
完成覆盖。 - getName();
因为上一步Foo().getName();
执行完成后,变成window.getName()
输出1
。 - new Foo.getName();
由于优先级的关系,.
语法优先级高于new
,相当于new (Foo.getName) ()
,实际上将getName
函数作为了构造函数来执行,执行new 2
输出2
。 - new Foo().getName();
先执行创建构造函数new Foo()
,再用实例里的getName
方法,实例不能调用构造函数中方法,去原型对象中找到这个方法输出3
。 - new new Foo().getName();
相当于new ((new Foo()).getName)
在6的基础上,new 3
输出结果3
。