作用域就是变量与函数的可访问范围。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下 3 种情形拥有全局作用域。
var outVariable = "我是最外层变量"; //最外层变量
function outFun() { //最外层函数
var inVariable = "内层变量";
function innerFun() { //内层函数
console.log(inVariable);
}
innerFun();
}
console.log(outVariable); //我是最外层变量
outFun(); //内层变量
console.log(inVariable); //inVariable is not defined
innerFun(); //innerFun is not defined
function outFun2() {
variable = "未定义直接赋值的变量";
var inVariable2 = "内层变量2";
}
outFun2();//要先执行这个函数,否则根本不知道里面是啥
console.log(variable); //未定义直接赋值的变量
console.log(inVariable2); //inVariable2 is not defined
window.name
、window.location
、window.top
等。和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域称为函数作用域 。
如 1. 中的变量 inVariable
和函数 innerFun
都只拥有局部作用域。
var a = 10; // 全局
(function() {
var b = 20;// 函数
})();
console.log(a); // 10
console.log(b); // error, b in not defined
eval("var a = 1;"); // eval
for (var item in {
a: 1,
b: 2
}) {
console.log(item);
}
console.log(item); // item still in scope
在 JavaScript 中,函数也是对象,实际上,JavaScript 里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供 JavaScript 引擎访问的内部属性。其中一个内部属性是 [[Scope]],由 ECMA-262 标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
在函数执行过程中,每遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。函数执行过程中,每个标识符都要经历这样的搜索过程。
举一个作用域链的例子。
var outVariable = "我是最外层变量"; //最外层变量
function outFun() { //最外层函数
var inVariable = "内层变量";
function innerFun() { //内层函数
console.log(inVariable);
var tempVariable = inVariable;
}
innerFun();
}
对最开始的代码稍加修改
其作用域链为:
window
├──outVariable
└──outFun()
├──inVariable
└──innerFun()
└──tempVariable
对于 innerFun()
,其作用域链包含 3 个对象:innerFun() 自己的变量对象、outFun()的变量对象、全局变量对象。
function outer() {
var i = 1;
var func = new Function("console.log(typeof i);");
func(); // undefined
}
outer();
通过构造器创建的函数是访问不到外层的局部变量的。
有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。有两种情况下会发生这种现象。
try-catch
语句中的 catch
块with
语句对 with 来说,将会指定对象添加到作用域链中。对 catch 来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
从作用域链的结构可以看出,在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。因为全局变量总是存在于运行期上下文作用域链的最末端,因此在标识符解析的时候,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。一个好的经验法则是:如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。
function foo(x, y, z) {
function x() {};
console.log(x);
}
foo(100); // funtion x()
function foo2(x, y, z) {
var x = 50;
console.log(x);
}
foo2(110); // 50
function foo3() {
function x() {};
var x = 100;
console.log(x);
}
foo3(); // 100
function foo4() {
function x() {};
var x;
console.log(x);
}
foo4(); // funtion x()
注:函数表达式不会影响 VO
给 VO 对象内的变量赋值。
VO 变为 AO (活动对象 activation object)。
alert(x); // function
var x = 10;
alert(x); // 10
x = 20;
function x() {}
alert(x); // 20
if (true) {
var a = 1;
} else {
var b = true;
}
alert(a); // 1
alert(b); // undefined
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有