要搞清楚一个变量的作用域,重点是搞清楚预解析顺序,然后再判断作用域的范围,这些都是有套路可言:先找本层环境有无声明,有的话,看是否进行了赋值;只有声明没有执行赋值,就是undefined。没有声明也没有赋值的话,就再向上一层查找,直到找到为止。如果所有的执行环境都没有找到,那么控制台就会报错变量找不到。
函数的话就更简单了:找本层环境是否有预解析的函数,有的话即可执行。没有的话还是向上查找。
6.什么是函数的作用域链 ?
javascript中,变量的作用域有全局作用域和局部作用域两种。
a.全局作用域:在代码任何地方都能访问到的对象拥有全局作用域
b.局部作用域:一般只在固定的代码片段内可以访问到。
c.函数作用域链:在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
javascript中,变量的作用域有全局作用域和局部作用域两种。
a.全局作用域:在代码任何地方都能访问到的对象拥有全局作用域
b.局部作用域:一般只在固定的代码片段内可以访问到。
c.函数作用域链:在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
1.全局变量:
(1)在全局范围内声明的变量,如var a=1;
(2)只有赋值没有声明的值,如a=2;
(注:如果a=2在函数环境中,也是全局变量)
2.局部变量:
写入函数中的变量,叫做局部变量。
应用场景(一些常见的作用域相关的面试题):
1
2
3
4
5
6
7
8
|
var
a=
"aa"
;
function
test(){
alert(a);
//undefined,函数执行后,在函数环境内,var a会预解析,当弹出a时,首先先找本层环境内有无声明,发现有。但是代码没有执行到赋值,所以结果是undefined。
var
a=
"bb"
;
//var a会预解析在函数开头,执行到这行才进行赋值
alert(a);
//“bb”
}
test();
alert(a);
//"aa" 找全局环境下的声明,找到了var a="aa"
|
1
2
3
4
5
6
7
|
var
a=
"aa"
;
function
test(){
alert(a);
//“aa”,函数执行后,在函数环境内,没有找到本层环境关于a的声明,所以开始向上一层环境查找。
a=
"bb"
;
//执行到这行开始改变全局a的量
}
test();
alert(a);
//"bb" 全局环境的a在函数执行时已经被改变
|
1
2
3
4
5
6
7
8
9
10
|
function
test(){
b();
//函数b会被预解析,因此可以调用,执行了输出1;
var
a=1;
function
b(){
console.log(1);
console.log(a);
//undefined
var
a=2;
}
}
test();
|
alert(foo); // function foo() {}
alert(bar); // undefined
function foo() {}
var bar = function bar_fn() {};
alert(foo); // function foo() {}
alert(bar); // function bar_fn() {}
输出结果分别是function foo() {}
、undefined
、function foo() {}
和function bar_fn() {}
。
可以看到 foo
的声明是写在 alert
之后,仍然可以被正确调用,因为 JavaScript 解释器会将其提升到 alert
前面,而以函数表达式创建的函数 bar
则不享受此待遇。
那么bar
究竟有没有被提升呢,其实用 var
声明的变量都会被提升,只不过是被先赋值为 undefined
罢了,所以第二个 alert
弹出了 undefined
。
var a = 1
function fn1(){
function fn3(){
function fn2(){
console.log(a)
}
var a
fn2()
a = 4
}
var a = 2
return fn3
}
var fn = fn1()
fn() //输出undifined
var a = 1
function fn1(){
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
function fn2(){
console.log(a)
}
var fn = fn1()
fn() // 输出1
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
var fn = fn1()
fn() //输出2
以下代码输出什么? 写出作用域链查找过程伪代码
var a = 1;
function fn(){
console.log(a) //(1)
var a = 5
console.log(a) //(2)
a++
var a
fn3() //(3)
fn2() //(4)
console.log(a) //(5)
function fn2(){
console.log(a)
a = 20
}
}
function fn3(){
console.log(a)
a = 200
}
fn()
console.log(a) //(6)
- 输出结果:从上到下依次是undefined、5、1、6、20、200
- 查找过程:
- 调用函数fn,进入fn的执行环境,var a变量声明前置并初始化a为undefined,所以(1)处打印undefined;
- 给a赋值5,所以(2)处打印5;
- 继续执行a++,得到a为6留到下次使用
- 继续执行函数fn3(),进入fn3作用域,fn3内没有声明变量a于是向外层查找,而fn(3)所处作用域为全局作用域,无论它在哪执行,永远都是打印的全局变量1,所以(3)处打印1,继续向下读取a=200,fn3不存在函数嵌套的情况,里面的a也未被声明,所以a是一个全局变量,全局中声明了变量a,于是替换全局变量a的值1为200,所以(6)处打印200;
- 接着调用fn2函数,进入fn2执行环境,没有找到变量a,于是向外层函数fn中查找,得到了上上一步中的值6,所以(4)处打印6,继续向下读取a=20,a未被声明于是会逐级向上查找变量a,在父函数fn中找到了变量a并赋值20,所以(5)处打印20。
分析:fn3改变了全局变量a的值,fn2改变了fn中局部变量a的值。作用域(执行环境)全局变量是相对的。
作者:_Dot大师兄
链接:https://www.jianshu.com/p/ac38114b6e24
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:萧雪圣
链接:https://www.jianshu.com/p/888fded98527
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:betterwlf
链接:https://www.jianshu.com/p/70a7c75b863e
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。