JS:谈谈JS中的作用域?

answer

JavaScript中的作用域(Scope)是指代码在何处以及如何查找变量的规则。作用域决定了代码中变量的可访问性和生命周期。了解作用域对于编写正确和高效的JavaScript代码非常重要。

作用域的类型

  1. 全局作用域(Global Scope)

    • 全局作用域是指在任何函数之外定义的变量,这些变量可以在代码的任何地方访问。
    • 在浏览器环境中,全局作用域的全局对象是window,在Node.js中是global
    javascript 复制代码
    var globalVar = 'I am a global variable';
    
    function foo() {
      console.log(globalVar); // 可以访问到 globalVar
    }
    
    foo();
  2. 函数作用域(Function Scope)

    • 函数作用域是指在函数内部定义的变量,这些变量只能在函数内部访问。
    • 使用var关键字声明的变量具有函数作用域。
    javascript 复制代码
    function bar() {
      var localVar = 'I am a local variable';
      console.log(localVar); // 可以访问到 localVar
    }
    
    bar();
    // console.log(localVar); // 报错,localVar 在函数外不可访问
  3. 块级作用域(Block Scope)

    • 块级作用域是指在块(由 {} 包围的代码块)内部定义的变量,这些变量只能在块内部访问。
    • 使用letconst关键字声明的变量具有块级作用域。
    javascript 复制代码
    if (true) {
      let blockVar = 'I am a block-scoped variable';
      console.log(blockVar); // 可以访问到 blockVar
    }
    
    // console.log(blockVar); // 报错,blockVar 在块外不可访问

作用域链(Scope Chain)

作用域链是指代码在当前作用域中查找变量时,如果当前作用域中没有找到,就会沿着作用域链向上查找,直到找到变量或到达全局作用域为止。如果到达全局作用域仍未找到,则变量未定义。

javascript 复制代码
var globalVar = 'global';

function outerFunction() {
  var outerVar = 'outer';

  function innerFunction() {
    var innerVar = 'inner';
    console.log(innerVar); // 可以访问 innerVar
    console.log(outerVar); // 可以访问 outerVar
    console.log(globalVar); // 可以访问 globalVar
  }

  innerFunction();
}

outerFunction();

变量提升(Hoisting)

变量提升是指JavaScript在代码执行之前会将变量声明提升到其作用域的顶部。需要注意的是,变量提升只会提升声明部分,不会提升赋值部分。

javascript 复制代码
console.log(hoistedVar); // 输出 undefined
var hoistedVar = 'I am hoisted';
console.log(hoistedVar); // 输出 'I am hoisted'

foo(); // 可以调用 foo 函数

function foo() {
  console.log('I am hoisted function');
}

闭包(Closure)

闭包是指函数能够记住并访问其定义时的词法作用域,即使这个函数是在其词法作用域之外执行的。闭包使得函数可以访问并操作其外部函数作用域中的变量。

javascript 复制代码
function outerFunction() {
  var outerVar = 'outer';

  return function innerFunction() {
    console.log(outerVar); // 可以访问 outerVar
  };
}

const inner = outerFunction();
inner(); // 输出 'outer'

块级作用域与函数作用域的区别

  1. var 声明的变量具有函数作用域,而 letconst 声明的变量具有块级作用域。
  2. var 声明的变量会被提升到函数作用域的顶部,而 letconst 声明的变量会被提升到块级作用域的顶部,但不会初始化。
javascript 复制代码
if (true) {
  var functionScopedVar = 'function scoped';
  let blockScopedVar = 'block scoped';
  const anotherBlockScopedVar = 'another block scoped';
}

console.log(functionScopedVar); // 输出 'function scoped'
console.log(blockScopedVar); // 报错,blockScopedVar 在块外不可访问
console.log(anotherBlockScopedVar); // 报错,anotherBlockScopedVar 在块外不可访问

总结

  • 全局作用域:在代码的任何地方都可以访问的变量。
  • 函数作用域:在函数内部声明的变量只能在函数内部访问。
  • 块级作用域 :在块内部声明的变量只能在块内部访问,使用 letconst 声明。
  • 作用域链:变量查找机制,从当前作用域逐级向上查找,直到全局作用域。
  • 变量提升 :变量声明会被提升到其作用域的顶部,var 声明的变量提升但不初始化,letconst 声明的变量提升但在声明前不可使用。
  • 闭包:函数能够记住并访问其定义时的词法作用域,即使在其词法作用域之外执行。

理解和正确使用作用域可以帮助开发者编写出更高效、易读和可维护的JavaScript代码。

追问:怎么理解词法作用域和动态作用域?

词法作用域(Lexical Scope)

词法作用域,也称静态作用域,是指变量的作用域在代码编写时就确定了。词法作用域依赖于代码的书写位置,与函数的调用方式无关。

在词法作用域中,一个函数的作用域链在函数定义时就确定了,而不是在函数调用时确定的。JavaScript采用的是词法作用域。

示例

javascript 复制代码
function outer() {
  const outerVar = 'I am from outer scope';

  function inner() {
    console.log(outerVar); // 可以访问 outerVar
  }

  inner();
}

outer();

在这个例子中,inner函数可以访问outer函数中的变量outerVar,因为inner函数在定义时就确定了其词法作用域,它会沿着作用域链向上查找变量。

动态作用域(Dynamic Scope)

动态作用域是指变量的作用域在函数调用时确定,而不是在代码编写时确定。在动态作用域中,变量的查找取决于函数从哪里调用,而不是从哪里定义。

大多数现代编程语言(包括JavaScript)都不采用动态作用域。少数语言,如早期的Lisp方言和Bash脚本语言,采用动态作用域。

示例(假想的动态作用域)

javascript 复制代码
let outerVar = 'I am from global scope';

function outer() {
  const outerVar = 'I am from outer scope';
  inner();
}

function inner() {
  console.log(outerVar); // 动态作用域:将输出什么取决于inner从哪里调用
}

outer(); // 如果 JavaScript 是动态作用域,这里会输出 'I am from outer scope'
inner(); // 如果 JavaScript 是动态作用域,这里会输出 'I am from global scope'

在动态作用域中,inner函数在outer函数内调用时,会找到outer函数中的变量outerVar,而在全局调用时,会找到全局作用域中的变量outerVar

JavaScript 中的词法作用域

JavaScript 采用词法作用域,因此变量的作用域在代码定义时就确定了,而不是在调用时确定的。

示例

javascript 复制代码
let outerVar = 'I am from global scope';

function outer() {
  const outerVar = 'I am from outer scope';

  function inner() {
    console.log(outerVar); // 词法作用域:始终会输出 'I am from outer scope'
  }

  inner();
}

outer(); // 输出 'I am from outer scope'
inner(); // 报错,inner 在全局作用域未定义

在JavaScript中,无论inner函数从哪里调用,它始终会输出outer函数中的变量outerVar的值,因为在定义inner函数时,outerVar就已在其词法作用域内。

总结

  • 词法作用域(Lexical Scope):作用域在代码编写时确定,变量的查找基于代码的书写位置。JavaScript采用词法作用域。
  • 动态作用域(Dynamic Scope):作用域在函数调用时确定,变量的查找基于函数的调用位置。JavaScript不采用动态作用域。

理解词法作用域和动态作用域的区别有助于正确理解变量查找机制及作用域链的工作原理,从而编写更清晰、正确的代码。

相关推荐
~废弃回忆 �༄15 分钟前
CSS中相对定位使用详情
前端·css·css中相对定位使用详情
inxunoffice28 分钟前
批量压缩与优化 Excel 文档,减少 Excel 文档大小
前端·excel
清羽_ls33 分钟前
react useEffect函数清除副作用函数执行时机
前端·javascript·react.js
蜜獾云36 分钟前
nginx: [error] invalid PID number ““ in “/usr/local/nginx/logs/nginx.pid“
java·前端·nginx
奶糖 肥晨1 小时前
基于 Vue 和 Element Plus 的时间范围控制与数据展示
前端·vue.js·elementui
難釋懷1 小时前
JavaScript数据类型简介
前端·javascript
怣疯knight1 小时前
网页缩放的html代码
前端·html
AntonCook1 小时前
前端AI探索之大模型变智能体
前端·ai 编程
Riesenzahn2 小时前
TypeScript中支持的访问修饰符有哪些?
前端·javascript
竣峰2 小时前
简单商品管理页开发-基于yzpass-admin-template 后台管理系统模版
前端·后端