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不采用动态作用域。

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

相关推荐
gnip13 小时前
企业级配置式表单组件封装
前端·javascript·vue.js
一只叫煤球的猫14 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
excel15 小时前
Three.js 材质(Material)详解 —— 区别、原理、场景与示例
前端
掘金安东尼16 小时前
抛弃自定义模态框:原生Dialog的实力
前端·javascript·github
hj5914_前端新手19 小时前
javascript基础- 函数中 this 指向、call、apply、bind
前端·javascript
薛定谔的算法19 小时前
低代码编辑器项目设计与实现:以JSON为核心的数据驱动架构
前端·react.js·前端框架
Hilaku20 小时前
都2025年了,我们还有必要为了兼容性,去写那么多polyfill吗?
前端·javascript·css
yangcode20 小时前
iOS 苹果内购 Storekit 2
前端
LuckySusu20 小时前
【js篇】JavaScript 原型修改 vs 重写:深入理解 constructor的指向问题
前端·javascript
LuckySusu20 小时前
【js篇】如何准确获取对象自身的属性?hasOwnProperty深度解析
前端·javascript