JS 作用域链
JavaScript的作用域链是指在JavaScript中,每个函数都有一个自己的作用域,当访问一个变量时,JavaScript引擎会先在当前函数的作用域中查找该变量,如果找不到,就会到该函数的父级函数的作用域中查找,直到找到该变量或者到全局作用域中仍未找到。
举个例子来说,假设有如下代码:
function foo() {
var a = 1;
function bar() {
var b = 2;
console.log(a + b);
}
bar();
}
foo();
在这个例子中,当执行bar()
函数时,JavaScript引擎会先在bar()
函数的作用域中查找变量b
,如果找到了就直接使用;如果没找到,就会到bar()
函数的父级函数foo()
的作用域中查找,这时会找到变量a
,所以最终输出结果为3。
如果在bar()
函数中使用了一个变量c
,而在foo()
函数中并没有定义该变量,那么JavaScript引擎会一直向上查找到全局作用域,如果全局作用域中也没有定义该变量,那么程序就会抛出一个ReferenceError
异常。
补充一下关于作用域链的一些特点:
- 作用域链的::顶端是全局作用域,底端是当前函数的作用域::。
- 当::函数执行完毕后,它的作用域链会被销毁,其中的变量也会随之被销毁::。这也是为什么在函数外部无法访问函数内部定义的变量。
- 在ES6之前,JavaScript中只有函数作用域和全局作用域,没有块级作用域。因此在使用
for
循环等语句时需要特别注意变量的作用域问题。而在::ES6中引入了let
和const
关键字,使得JavaScript也具有了块级作用域::。 - ::作用域链是在函数定义时就已经确定的,与函数的调用位置无关::。这也是为什么在JavaScript中可以使用闭包来实现一些高级的功能,比如保存一个函数的内部状态等。
补充一下关于作用域链的一些应用场景:
- ::闭包:::通过在一个函数内部定义另一个函数,并返回该函数的引用,可以创建一个闭包,使得::内部函数可以访问外部函数的变量::。由于JavaScript中的::作用域链是基于函数定义时的作用域::,而不是函数调用时的作用域,因此内部函数可以在外部函数执行完毕后仍然访问到外部函数的变量。
- ::模块化:通过使用IIFE(立即调用函数表达式)来创建一个独立的作用域::,可以将一些变量和函数封装起来,避免与其他代码产生冲突。这种模式被广泛应用于现代JavaScript开发中,比如使用Webpack等工具打包代码时就会自动将每个模块放在一个独立的作用域中。
- 防止变量污染:由于JavaScript中只有函数作用域和全局作用域,因此在编写大型应用程序时需要特别注意避免变量名冲突。通过::使用IIFE等方式来创建独立的作用域,可以有效地避免变量名冲突::,从而提高代码的可维护性和可重用性。
IIFE(Immediately Invoked Function Expression)是JavaScript中的一种函数表达式,它立即执行函数并返回结果。IIFE常用于创建私有作用域,避免全局命名冲突,并且可以实现模块化的代码结构。
以下是IIFE的示例代码:
(function() {
// 在这里编写代码
})();
在上述代码中,
- 函数被包含在一对圆括号中,
- 并紧接着使用另一对圆括号调用函数。
这样就可以立即执行函数,并且函数内部的代码在定义后立即执行。
示例1:使用IIFE创建私有作用域并避免全局命名冲突
(function() {
var name = 'John';
function sayHello() {
console.log('Hello, ' + name + '!');
}
sayHello();
})();
// 在这个作用域外部,无法访问name和sayHello
console.log(name); // 报错:name is not defined
sayHello(); // 报错:sayHello is not defined
在上述代码中,变量name和函数sayHello都被定义在IIFE的作用域内部,因此在IIFE外部无法访问它们。这样可以避免全局命名冲突。
示例2:使用IIFE实现模块化的代码结构
var myModule = (function() {
var privateVariable = 'Private Variable';
function privateMethod() {
console.log('Private Method');
}
return {
publicMethod: function() {
console.log('Public Method');
},
publicVariable: 'Public Variable'
};
})();
myModule.publicMethod(); // 输出:Public Method
console.log(myModule.publicVariable); // 输出:Public Variable
console.log(myModule.privateVariable); // 输出:undefined
myModule.privateMethod(); // 报错:myModule.privateMethod is not a function
在上述代码中,IIFE返回一个包含公共方法和公共变量的对象。这样,通过myModule对象可以访问公共方法和公共变量,但无法访问私有变量和私有方法。
通过使用IIFE,可以创建私有作用域并避免全局命名冲突,同时实现模块化的代码结构。这在编写复杂的JavaScript应用程序时非常有用。
转载自:https://juejin.cn/post/7273026570941579303