js作用域链 作用域 一、概述 在 JavaScript 中,作用域(Scope)定义了变量、函数等标识符的可访问范围。它决定了在代码的哪个部分可以引用这些标识符。JavaScript 主要有两种作用域:全局作用域和局部作用域。
二、全局作用域
定义
当一个变量或函数在代码的最顶层定义时,它就处于全局作用域。在全局作用域中声明的变量和函数在整个程序中都可以访问。
例如,在浏览器环境中,全局作用域是 window 对象。在 Node.js 环境中,全局作用域是 global 对象。
示例 1 2 3 4 5 6 var globalVar = "I'm global" ;function globalFunc ( ) { console .log ("I'm a global function" ); } console .log (globalVar); globalFunc ();
在这个例子中,globalVar 和 globalFunc 都是在全局作用域中定义的。无论在代码的哪个位置,都可以访问它们。
三、局部作用域 (一)函数作用域
定义
函数作用域是最常见的一种局部作用域。在函数内部声明的变量和函数参数等只能在该函数内部访问,外部无法直接访问。
示例 1 2 3 4 5 6 function myFunction ( ) { var localVar = "I'm local" ; console .log (localVar); } myFunction ();console .log (localVar);
在这个例子中,localVar 是在 myFunction 函数内部定义的局部变量。当在函数外部尝试访问它时,就会出现引用错误。
(二)块级作用域(ES6 引入)
定义
块级作用域是指在代码块(用 {} 包围的部分)内声明的变量(使用 let 和 const 关键字)只能在该代码块内访问。这是 ES6 新增的特性,使得 JavaScript 的作用域更加灵活。
示例 1 2 3 4 5 6 7 8 { let blockVar = "I'm block scoped" ; const blockConst = "I'm also block scoped" ; console .log (blockVar); console .log (blockConst); } console .log (blockVar); console .log (blockConst);
在这个例子中,blockVar 和 blockConst 是在代码块内使用 let 和 const 声明的变量。它们只能在该代码块内被访问,一旦出了代码块范围,就无法访问了。
四、作用域链
概念
当在某个作用域中访问一个变量时,如果该作用域内没有找到这个变量,就会沿着作用域链向上查找,直到找到该变量或者到达全局作用域为止。作用域链是由多个作用域组成的链状结构,每个作用域都指向它的父作用域。
示例 1 2 3 4 5 6 7 8 9 10 11 12 var globalVar = "global" ;function outerFunction ( ) { var outerVar = "outer" ; function innerFunction ( ) { var innerVar = "inner" ; console .log (innerVar); console .log (outerVar); console .log (globalVar); } innerFunction (); } outerFunction ();
在这个例子中,innerFunction 可以访问自己的局部变量 innerVar,也可以访问它的父作用域 outerFunction 中的变量 outerVar,还可以访问全局作用域中的变量 globalVar。这就是作用域链在起作用,它使得嵌套的函数能够访问外层函数的变量。
五、闭包 js闭包
定义
闭包是一个函数和其周围的状态(词法环境)的组合。闭包让你可以从内部函数访问外部函数作用域中的变量。闭包是 JavaScript 中一个非常强大的特性,它使得函数可以“记住”并访问其创建时所在的作用域链中的变量,即使该函数在其创建上下文之外执行。
示例 1 2 3 4 5 6 7 8 9 10 function createCounter ( ) { var count = 0 ; return function ( ) { count += 1 ; return count; }; } var counter = createCounter ();console .log (counter ()); console .log (counter ());
在这个例子中,createCounter 函数返回了一个匿名函数。这个匿名函数可以访问 createCounter 函数中的变量 count。即使 createCounter 函数执行完毕,返回的匿名函数仍然可以访问 count 变量,这就是闭包的作用。每次调用返回的匿名函数时,count 的值都会增加,因为闭包“记住”了 count 变量的状态。
js中怎么区分作用域 在 JavaScript 中,作用域(Scope) 决定了变量、函数和对象的可访问范围。作用域的划分主要依赖于变量声明的方式(如 var、let、const)以及代码的结构(如函数、块级作用域)。以下是详细区分作用域的方法:
一、作用域的类型
作用域类型
特点
示例代码
全局作用域
在函数或块外声明的变量,全局可访问
let globalVar = 10;(全局变量)
函数作用域
在函数内部声明的变量(使用 var),仅函数内可访问
function fn() { var a = 1; }(a 仅在 fn 内有效)
块级作用域
在 {} 内声明的变量(使用 let/const),仅块内有效
if (true) { let b = 2; }(b 仅在 if 块内有效)
模块作用域
ES6 模块(<script type="module">)中的顶层变量,仅在模块内有效
export const c = 3;(模块外不可见)
二、区分作用域的关键规则 1. var 的函数作用域
var 声明的变量只有函数作用域 ,没有块级作用域。
在 if、for 等块中使用 var,变量会“泄漏”到外层作用域。
1 2 3 4 5 6 function exampleVar ( ) { if (true ) { var x = 10 ; } console .log (x); }
2. let/const 的块级作用域
let 和 const 声明的变量只在当前代码块({})内有效 。
常见于 if、for、while 等块级结构中。
1 2 3 4 5 6 7 function exampleLet ( ) { if (true ) { let y = 20 ; const z = 30 ; } console .log (y); }
3. 函数作用域 vs 块级作用域
函数作用域 :使用 var 在函数内部声明,变量在整个函数内有效。
块级作用域 :使用 let/const 在 {} 内声明,变量仅在块内有效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function funcScope ( ) { var a = 1 ; if (true ) { var a = 2 ; } console .log (a); } function blockScope ( ) { let b = 1 ; if (true ) { let b = 2 ; } console .log (b); }
三、作用域链(Scope Chain) JavaScript 查找变量时会沿着作用域链逐级向上查找:
当前作用域 → 外层函数作用域 → 全局作用域。
闭包(Closure) :内部函数保留对外部函数作用域的引用。
1 2 3 4 5 6 7 8 9 function outer ( ) { let outerVar = "Outer" ; function inner ( ) { console .log (outerVar); } return inner; } const innerFn = outer ();innerFn ();
四、常见场景区分 1. 循环中的 var vs let
var 在循环中会共享同一个变量。
let 在每次循环中创建一个新的块级作用域。
1 2 3 4 5 6 7 8 for (var i = 0 ; i < 3 ; i++) { setTimeout (() => console .log (i)); } for (let j = 0 ; j < 3 ; j++) { setTimeout (() => console .log (j)); }
2. 模块作用域
在 ES6 模块中,顶层变量默认属于模块作用域,不会污染全局。
1 2 3 4 let moduleVar = "模块内变量" ; export { moduleVar };
五、总结:如何区分作用域?
检查声明方式 :
var → 函数作用域或全局作用域。
let/const → 块级作用域。
观察代码结构 :
函数内部 → 函数作用域。
{} 包裹的代码块(如 if、for)→ 块级作用域(需配合 let/const)。
模块化环境 :
使用 <script type="module"> 或 ES6 模块语法 → 模块作用域。
通过理解声明方式和代码结构,可以清晰区分 JavaScript 的作用域,避免变量污染和作用域混淆的问题。
() 在 JavaScript 中,圆括号 () 本身不会创建新的作用域 ,但括号内的内容可能属于不同的作用域,具体取决于其所在的上下文。以下是常见使用场景的详细分析:
一、函数参数列表中的 () 函数定义或调用时的参数列表 () 不会创建作用域 ,参数变量属于函数体作用域的一部分:
1 2 3 4 5 function sum (a, b ) { return a + b; } sum (1 , 2 );
二、表达式分组中的 () 圆括号用于表达式分组时,不改变作用域 :
1 2 const result = (1 + 2 ) * 3 ;
三、立即调用函数表达式(IIFE)中的 () 通过 () 包裹函数并立即调用(IIFE),会创建一个新的函数作用域 :
1 2 3 4 (function ( ) { var privateVar = "内部变量" ; })(); console .log (privateVar);
四、条件语句中的 () 条件语句(如 if、while)中的条件表达式 () 不创建作用域 :
1 2 3 4 5 if (true ) { let x = 10 ; } console .log (x);
五、箭头函数参数中的 () 箭头函数的参数列表 () 不会创建新作用域 ,参数变量属于箭头函数体作用域:
1 2 3 4 const greet = (name ) => { console .log (`Hello, ${name} ` ); }; greet ("Alice" );
六、对象属性或方法中的 () 对象方法调用时的 () 属于方法调用语法 ,不影响作用域:
1 2 3 4 5 6 7 const obj = { name : "Bob" , sayHi : function ( ) { console .log (this .name ); } }; obj.sayHi ();
七、特殊语法中的 () 某些语法(如 for 循环的初始化部分)中的 () 可能隐含块级作用域 :
1 2 3 4 for (let i = 0 ; i < 3 ; i++) { } console .log (i);
总结:如何区分 () 内的作用域?
括号本身不创建作用域 ,真正的作用域由代码结构(如函数、块级 {})决定。
关键场景:
函数调用或定义 :参数列表属于函数体作用域。
IIFE :括号包裹的函数会创建独立作用域。
表达式或条件 :括号仅用于语法分组,不影响作用域。
示例对比
场景
括号作用
是否创建新作用域
function(a, b) { ... }
参数列表
否(属于函数体)
(function() { ... })()
IIFE
是(函数作用域)
if (condition) { ... }
条件表达式
否(由 {} 决定)
(1 + 2) * 3
表达式分组
否
理解作用域的核心是关注代码的结构(如 {}、函数、模块),而非单纯的 ()。