JS课程学习之作用域

131 阅读3分钟

先来看一段代码:

alert(a); //undefined 
alert(b); //undefined
alert(c); //报错,Uncaught ReferenceError: c is not defined
alert(d); //报错,Uncaught ReferenceError: c is not defined


var a=1;
if(false){  //按顺序扫描时,当作这一行不存在的,依然把b加入到window中去
    var b=2;
}else{
    c=3;
}function f() {  //f是window对象的一个全局成员,d的话是f的内容,不会出现在window中去
    var d=4;
}

我们来分析以下上述代码的输出。

用var定义的变量a,会被添加到词法作用域中去,因此输出为undefined;
用var定义的变量b,也是一样的undefined;
全局变量c,不会被添加到词法作用域中,在执行之前调用时,会报错;
用var定义的变量d,因为d在f中,f在window中,所以d不会在window中,也就是不会被添加到词法作用域中去。

什么是作用域

作用域确定了一个变量,函数,成员在程序中可以被访问到的范围;也可以用来做信息隐藏(如上述代码中的d,就相当于被隐藏起来了)

块作用域

用{}包起来的部分,即为块

for (var i=0; i<3; i++){
    
}
alert(i);//3

这说明了js中好像没有块级作用域的概念。否则i将只在for循环中生效,而不会被alert访问到。
但是ES6提供的新的变量定义方式let解决了js中块级作用域的问题

for (let i=0; i<3; i++){
    
}
alert(i);//3

此时报错:Uncaught ReferenceError: i is not defined。即用let声明变量后,被声明的变量只在它的块级作用域内能被访问到。

函数作用域

function f(){
var x;//变量x只在函数内部生效
}

动态作用域

function f(){
    alert(x);
}
function f1() {
    var x=5;
    f();
}
f1();//调用函数f1之后报错:x is not defined.也就是说,函数f1中的f访问不到上一行的x=5;

所以js是没有动态作用域的概念的,本身是静态作用域

静态作用域(也称作词法作用域或者闭包)

仍以上个代码为例:解析器执行代码的时候,会给f创建一个scope,它指向创建这个f时候的词法环境,即f.scope-->f.le,而创建f时候的词法环境是window,也就是说,内部有一个我们看不见的scope指向window
真正执行代码的时候,f会创建自己的词法环境,会和f的作用域scope本身会关联起来。

【这其实也就解释了上段代码为何会报错:调用f的时候,产生scope,去window中找要alert的x,发现window中没有,所以就报错了】
那么我们在全局中增添一个变量即可:

var x=100;

function f(){
    alert(x);
}
function f1() {
    var x=5;
    f();
}
f1();  //此时输出为100;

js的作用域的链条

function f(){ //创建f时,scope==window
//执行时,创建自己的词法环境,简称为f.le;此时f.le--> f.[[scope]]
    var x=100;
    function g() {  //g.scope --> f.le
        //g.le --> g.scope
}
g();
}

new Function创建的函数,它的作用域永远指向全局

function f() {
    var x=100;
    var g=new Function("","alert(x)");
    g();
}
f();//此时报错,x is not defined

只有当全局中存在x变量,才能被读取

var x=2
function f() {
    var x=100;
    var g=new Function("","alert(x)");
    g();
}
f();//2

作用域的本质和用途

先在函数本身词法环境中找,再到父函数的此法环境中找,再到window中找。 用途就是可以隐藏变量,减少冲突。

(function () {
    var a=5;
    var b=6;
    function f(){
        alert(a);
    }
    window.f = f;
})();
f();//5

此时的变量a,b就相当于被隐藏起来了。