作用域
分为全局作用域,函数作用域,块级作用域(es6新增)
- js中首先有一个最外层的作用域,全局作用域;
- js中可以通过函数来创建一个独立作用域称为函数作用域,函数可以嵌套,所以作用域也可以嵌套;
- es6中新增了块级作用域(大括号,比如:if{},for(){},while(){}…)只适用于const let;
作用域链
自由变量
当前作用域没有定义的变量,但是被使用了,就会一层一层往上查找,直到找到为止;如果到全局作用域都没有找到,则会报错
作用域概念:自由变量的向上级作用域一层一层查找,直到找到为止,最高找到全局作用域,就形成了作用域链。(采用就近原则)
let a = 10
function fn1() {
let b = 20
function fn2() {
let c= 50
console.log(b); // 20
}
fn2()
}
fn1()
-------------------------------------------
let a = 10
function fn1() {
let b = 20
function fn2() {
let c= 50
let b = 40
console.log(b); // 40 就近原则
}
fn2()
}
fn1()
作用域面试题
比较常见的面试题:
<body>
<p>标题一</p>
<p>标题二</p>
<p>标题三</p>
<p>标题四</p>
</body>
</html>
<script>
var oPs = document.querySelectorAll('p')
for(var i = 0;i<oPs.length;i++) {
oPs[i].onclick = function() {
console.log(i);
}
}
</script>
问题:这时候我们发现不管我们点击哪一个控制台中都会输出4
原因:因为我们还没有点击之前for循环已经执行完了
解决:可以使用闭包和let解决
// 方法一:let具有块级作用域
var oPs = document.querySelectorAll('p')
for(let i = 0;i<oPs.length;i++) {
oPs[i].onclick = function() {
console.log(i);
}
}
// 方法二:利用闭包
var oPs = document.querySelectorAll('p')
for(let i = 0;i<oPs.length;i++) {
(function(i){
oPs[i].onclick = function() {
console.log(i);
}
})(i)
}
变量提升(预解析)
js是单线程的,代码在运行之前,会把带有var和function声明的变量提升到作用域的最前面;let const不存在变量提升
注意:函数的预解析要高于变量的预解析
变量提升
console.log(a); // undefined
var a = 10;
console.log(a); // 10
相当于:
var a;
console.log(a); // undefined
a = 10; // 此时才完成赋值
console.log(a); // 10
函数提升
以下函数相当于把整个fn提到作用域的最上面,可以正常打印。
fn()
function fn(){
console.log(1);
}
不过函数表达式不行,js会把var fn提升到作用域最前面,没有把函数提上去,所以会报错
fn() // 报错fn is not a function
var fn = function fun(){
console.log(1);
}
变量提升面试题
var x = 30;
function test(){
alert(x); // function x(){}
var x = 10;
alert(x); // 10
x=20;
function x(){
};
alert(x); // 20
}
test();
test是函数,每个函数作用域也首先要进行预解析
函数的预解析会高于变量的预解析,所以从上往下执行,
先弹出function x(){}
第二个alert(x)向上找,找到var x = 10;弹出10
第三个alert(x)向上找,找到x = 20;弹出20