JavaScript变量作用域深度解析:从全局到闭包

作者:闭包旅人

时间:2026-10-30

浏览:546

本文深入解析JavaScript中的变量作用域机制,涵盖全局与局部变量的区别、var声明的影响、函数作用域特性以及闭包的实现原理。通过具体代码示例,阐明变量在不同作用域下的可见性与生命周期,帮助开发者避免作用域错误并优化代码结构。

文章目录

JavaScript变量作用域基础

变量作用域指的是在程序中定义该变量的有效区域。首先通过一段示例代码来直观感受这一概念:

/* 代码1 */


var scope = "global ";
function checkScope() {
 var scope = "local ";
 function childCheck() {
  var scope = "childLocal ";
  document.write(scope);
 }
 function childUndefined() {
  document.write(scope);
  var scope;
 }
 function childOverride() {
  scope = "childOverride ";
  document.write(scope);
 }
 document.write(scope); //输出"local"
 childCheck();   //输出"childLocal"
 childUndefined();  //输出"undefined"
 childOverride();  //输出"childOverride"
 document.write(scope); //输出"childOverride"
}
checkScope();    //输出"local childLocal undefinedchildOverride childOverride"
document.write(scope);  //输出"global "

全局与局部作用域

全局作用域与局部作用域 全局(global)变量的作用域覆盖整个程序,在JavaScript中任何位置均可访问;而函数内部声明的变量属于局部(local)变量,其作用域仅限于该函数体内部。对于以下输出结果,读者应不会产生疑问: /* 代码2 */


var scope = "global";
function checkScope() {
 var scope = "local";
 document.write(scope);
}
checkScope();   //输出"local"
document.write(scope); //输出"global"

在全局作用域中,变量可以不使用var语句直接赋值,但在声明局部变量时必须使用var语句,否则该变量会被视为对全局变量的引用。参考以下代码: /* 代码3 */


var scope = "global";
function checkScope() {
 scope = "local";
 document.write(scope);
}
checkScope();   //输出"local"
document.write(scope); //输出"local"

函数作用域与块级作用域缺失

没有块作用域 JavaScript传统上不具备块级作用域(ES6前),即在函数中声明的变量在整个函数范围内都有效。对于以下代码,初学者可能会感到意外: /* 代码4 */


var scope = "global";
function checkScope() {
 document.write(scope); //语句4.1
 var scope = "local"; //语句4.2
 document.write(scope);
}
checkScope();   //输出"undefinedlocal"

由于语句4.1(var scope = "local";)声明的变量在整个checkScope函数作用域内均有效,因此在执行语句4.2(document.write(scope); )时,scope引用的是局部变量。此时局部变量scope尚未被赋值,因此输出结果为“undefined”。因此,良好的编程习惯是将所有变量声明集中放在函数开头。

对象属性变量

理解上述内容后,再回顾代码1便不再困惑。 对象的属性变量 对象属性变量的作用域相对直观,参考以下代码: /* 代码5 */


var scope = "global ";
var obj = new Object();
obj.scope = "object ";
obj.checkScope = function () {
 var scope = "loacl ";
 document.write(scope);   //输出"loacl"
 document.write(this.scope);  //输出"object"
 document.write(window.scope); //输出"global"
}
obj.checkScope(); //输出"loacl object global"

作用域机制详解

所谓作用域,即变量在代码块中的有效范围。若不理解JavaScript的作用域机制,调试代码时可能会遇到较大困难。

在函数中,若使用var声明变量,则该变量的作用域仅限于该函数内部,函数外部的代码无法访问该变量。如果在该函数内部再声明一个函数,内部函数可以访问外部函数的变量。

反之,如果声明变量时未使用var,则该变量的作用域不局限于当前函数。JavaScript引擎会在全局范围内检查该变量是否已定义。若未定义,则将其定义为全局变量。

函数可以访问相同作用域中的变量:


var foo = 'hello';

var sayHello = function() {
 console.log(foo);
};

sayHello();   // logs 'hello'
console.log(foo); // also logs 'hello'

变量作用域之外的代码无法访问该变量:


var sayHello = function() {
 var foo = 'hello';
 console.log(foo);
};

sayHello();   // logs 'hello'
console.log(foo); // doesn't log anything

不同作用域中名称相同的变量具有不同的值:


var foo = 'world';

var sayHello = function() {
 var foo = 'hello';
 console.log(foo);
};

sayHello();   // logs 'hello'
console.log(foo); // logs 'world'

函数定义后可以观察到函数内变量值的变化:


var myFunction = function() {
 var foo = 'hello';

 var myFn = function() {
  console.log(foo);
 };

 foo = 'world';

 return myFn;
};

var f = myFunction();
f(); // logs 'world' -- haha

闭包与作用域穿越

作用域也会穿越 — 闭包


// 一个自执行的匿名函数
(function() {
 var baz = 1;
 var bim = function() { alert(baz); };
 bar = function() { alert(baz); };
})();

console.log(baz); // 在函数外面不能访问 baz

bar(); // 声明 bar 的时候并没有用 var
  // 所以 bar 是一个全局变量; 但是,
  // bar 和 baz 在相同的作用域内被定义,
  // 所以 bar 可以访问 baz
  // 其实 bar 是个闭包函数

bim(); // bim 的作用域只限于匿名函数内部,
  // 所以这里不能调用

综合

所谓作用域,即变量在代码块中的有效范围。若不理解JavaScript的作用域机制,调试代码时可能会遇到较大困难。

在函数中,若使用var声明变量,则该变量的作用域仅限于该函数内部,函数外部的代码无法访问该变量。如果在该函数内部再声明一个函数,内部函数可以访问外部函数的变量。

反之,如果声明变量时未使用var,则该变量的作用域不局限于当前函数。JavaScript引擎会在全局范围内检查该变量是否已定义。若未定义,则将其定义为全局变量。

函数可以访问相同作用域中的变量:


var foo = 'hello';

var sayHello = function() {
 console.log(foo);
};

sayHello();   // logs 'hello'
console.log(foo); // also logs 'hello'

变量作用域之外的代码无法访问该变量:


var sayHello = function() {
 var foo = 'hello';
 console.log(foo);
};

sayHello();   // logs 'hello'
console.log(foo); // doesn't log anything

不同作用域中名称相同的变量具有不同的值:


var foo = 'world';

var sayHello = function() {
 var foo = 'hello';
 console.log(foo);
};

sayHello();   // logs 'hello'
console.log(foo); // logs 'world'

函数定义后可以观察到函数内变量值的变化:

 

var myFunction = function() {
 var foo = 'hello';

 var myFn = function() {
  console.log(foo);
 };

 foo = 'world';

 return myFn;
};

var f = myFunction();
f(); // logs 'world' -- haha

作用域也会穿越 — 闭包


// 一个自执行的匿名函数
(function() {
 var baz = 1;
 var bim = function() { alert(baz); };
 bar = function() { alert(baz); };
})();

console.log(baz); // 在函数外面不能访问 baz

bar(); // 声明 bar 的时候并没有用 var
  // 所以 bar 是一个全局变量; 但是,
  // bar 和 baz 在相同的作用域内被定义,
  // 所以 bar 可以访问 baz
  // 其实 bar 是个闭包函数

bim(); // bim 的作用域只限于匿名函数内部,
  // 所以这里不能调用

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章