JavaScript作用域
在JavaScript中,变量的作用域有全局作用域和局部作用域两种
1. 全局作用域(Global Scope)
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
(1)最外层函数和在最外层函数外面定义的变量拥有全局作用域,例如:
1 var jotaro = "承太郎"; 2 function doSomething() { 3 var joseph = "乔瑟夫"; 4 function innerSay() { 5 console.log(joseph) 6 } 7 innerSay(); 8 } 9 console.log(jotaro) //承太郎 10 console.log(joseph) //error 11 doSomething(); //乔瑟夫 12 innerSay() //error
(2)所有末定义直接赋值的变量自动声明为拥有全局作用域,例如:
1 function doSomething() { 2 var jotaro = "承太郎"; 3 joseph = "乔瑟夫"; 4 console.log(jotaro) 5 } 6 doSomething(); //承太郎 7 console.log(joseph) //乔瑟夫 8 console.log(jotaro) //脚本错误
变量joseph拥有全局作用域,而jotaro在函数外部无法访问到。
(3)所有window对象的属性拥有全局作用域
一般情况下,window对象的内置属性都拥有全局作用域,例如window.name、window.location、window.top等等。
2. 局部作用域(Local Scope)
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域称为函数作用域,例如下列代码中的blogName和函数innerSay都只拥有局部作用域。
1 function doSomething() { 2 var joseph = "乔瑟夫"; 3 function innerSay() { 4 console.log(joseph) 5 } 6 innerSay(); 7 } 8 console.log(joseph) //脚本错误 9 innerSay(); //脚本错误
作用域链(Scope Chain)
1 var name1 = "haha"; 2 function changeName() { 3 var name2 = "xixi"; 4 function swapName() { 5 console.log(name1);//haha 6 console.log(name2);//xixi 7 var tempName = name2; 8 name2 = name1; 9 name1 = tempName; 10 console.log(name1);//xixi 11 console.log(name2);//haha 12 console.log(tempName);//xixi 13 } 14 swapName(); 15 console.log(name1);//haha 16 console.log(name2);//xixi 17 //console.log(tempName); 18 } 19 changeName(); 20 console.log(name1);//xixi 21 //console.log(name2); 抛出错误:Uncaught ReferenceError: name2 is not defined 22 //console.log(tempName);抛出错误:Uncaught ReferenceError: tempName is not defined
上述代码中,一共有三个执行环境:全局环境、changeName()的局部环境和 swapName() 的局部环境。所以,
1.函数 swapName()的作用域链包含三个对象:自己的变量对象----->changeName()局部环境的变量对象 ----->全局环境的变量对象。
2.函数changeName()的作用域包含两个对象:自己的变量对象----->全局环境的变量对象。
就上述程序中出现的变量和函数来讲(不考虑隐形变量):
1.swapName() 局部环境的变量对象中存放变量 tempName;
2.changeName() 局部环境的变量对象中存放变量 name2 和 函数swapName();
3.全局环境的变量对象中存放变量 name1 、函数changeName();
在swapName()的执行环境中,在执行第5句代码时,解析器沿着函数 swapName()的作用域链一级级向后回溯查找变量 name1,直到在全局环境中找到变量 name1.并输出在控制台上。同样,在执行第6句代码时,解析器沿着函数 swapName()的作用域链一级级向后回溯,在函数changeName()的变量对象中发现变量 name2.通过代码对 name1 和 name2进行交换,并输出在控制台上,根据结果我们发现,这两个变量的值确实交换了。因此我们可以得出结论,函数的局部环境可以访问函数作用域中的变量,也可以访问和操作父环境(包含环境)乃至全局环境中的变量。
在changeName() 的执行环境中,执行第15行和第16行代码时,可以正确地输出 name1 和 name2 和两个变量的值(调用了函数swapName(),所以俩变量的值已相互交换),那是因为 name1 在changName()的父环境(全局环境)中, name2 在他自己的局部环境中,即 name1 和 name2 都在其作用域链上。但当执行第17行代码是发生错误 tempName is not defined。因为解析器沿着 函数changeName()的作用域链一级级的查找 变量 tempName时,并不能找到该变量的存在(变量 tempName不在其作用域链上),所以抛出错误。因此,我们可以得出结论:父环境只能访问其包含环境和自己环境中的变量和函数,不能访问其子环境中的变量和函数。
同理,在全局环境中,其变量对象中只存放变量 name1 、函数changeName(); 解析器只能访问变量 name1 和函数 changeName(), 而不能访问和操作 函数 changeName() 和函数 swapName() 中定义的变量或者函数。因此,在执行第21行和第22行代码时抛出变量没有定义的错误。所以说,全局环境只能访问全局环境中的变量和函数,不能直接访问局部环境中的任何数据。
其实,我们可以把作用域链想象成这样(里面的能访问外面的,外面的不能访问里面的,图为参考):
作用域链相关知识的总结:
1.执行环境决定了变量的生命周期,以及哪部分代码可以访问其中变量
2,执行环境有全局执行环境(全局环境)和局部执行环境之分。
3.每次进入一个新的执行环境,都会创建一个用于搜索变量和函数的作用域链
4.函数的局部环境可以访问函数作用域中的变量和函数,也可以访问其父环境,乃至全局环境中的变量和环境。
5.全局环境只能访问全局环境中定义的变量和函数,不能直接访问局部环境中的任何数据。
6.变量的执行环境有助于确定应该合适释放内存。
引用文章:https://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html
:https://www.cnblogs.com/buchongming/p/5858026.html