进击JS ——初入作用域

前言

作用域(Scope)在编程中是指变量的可访问性和可见性范围。作用域的概念对于理解变量的生命周期、避免命名冲突以及编写可维护的代码都至关重要。理解作用域有助于开发者更好地组织和管理程序中的变量,提高代码的可读性和可维护性。

闭包(Closure)是指在函数内部创建的函数,它可以访问到其外部函数作用域中的变量。换句话说,闭包可以"记住"并访问它被创建时所处的词法环境,即使在外部函数执行完毕后仍然可以访问这些变量。

JavaScript 中的闭包是一种非常强大且常见的编程模式,它常用于实现函数式编程中的许多概念,如部分应用、柯里化和高阶函数等。闭包在事件处理、定时器、模块模式等场景中也有广泛的应用。

介绍

javascript 复制代码
// 全局作用域
var globalVariable = "I'm a global variable";

function globalFunction() {
    console.log("Inside globalFunction: " + globalVariable); // 在函数内部可以访问全局变量
}

globalFunction();
console.log("Outside globalFunction: " + globalVariable); // 在函数外部也可以访问全局变量

// 局部作用域
function localFunction() {
    var localVariable = "I'm a local variable";
    console.log("Inside localFunction: " + localVariable); // 在函数内部可以访问局部变量
}

localFunction();
// console.log("Outside localFunction: " + localVariable); // 错误!无法在函数外部访问局部变量

// 块级作用域
if (true) {
    let blockScopedVariable = "I'm a block-scoped variable";
    console.log("Inside block: " + blockScopedVariable); // 在块级作用域内可以访问块级作用域变量
}

// console.log("Outside block: " + blockScopedVariable); // 错误!无法在块级作用域外部访问块级作用域变量

根据代码所展示的效果:

  1. 全局作用域(Global Scope): 全局作用域中的变量可以在代码的任何地方被访问,通常在程序的最外层定义。在整个程序执行过程中都是有效的。
  2. 局部作用域(Local Scope): 局部作用域中的变量只能在其被定义的范围内访问。比如在函数内定义的变量,只能在该函数内部被访问,称为局部变量。
  3. 块级作用域(Block Scope): 块级作用域是指由一对花括号 {} 所包围的区域,例如在循环、条件语句或者函数内部。在块级作用域中定义的变量只能在该块内部访问。

在一些现代编程语言中(如 JavaScript),引入了 letconst 关键字来声明块级作用域的变量,与传统的 var 关键字声明的变量不同,letconst 只在当前块级作用域内有效。

词法作用域

javascript 复制代码
// 全局作用域
var globalVariable = "I'm from global";

function outerFunction() {
    var outerVariable = "I'm from outer";

    function innerFunction() {
        var innerVariable = "I'm from inner";
        console.log(innerVariable); // 内部函数可以访问自身作用域内的变量
        console.log(outerVariable); // 内部函数可以访问外部函数作用域内的变量
        console.log(globalVariable); // 内部函数可以访问全局作用域内的变量
    }

    innerFunction(); // 调用内部函数
}

outerFunction(); // 调用外部函数

词法作用域(Lexical Scope)是指变量的作用域由它们在代码中的位置决定,与函数被调用的位置无关。

在这个例子中,innerFunction 可以访问到它的外部函数 outerFunction 和全局作用域中的变量,这是因为 JavaScript 使用词法作用域,函数的作用域是在定义时确定的,而不是在执行时。所以,innerFunction 在定义时就"记住了"它的词法环境,即 outerFunction 的作用域。

函数作用域

javascript 复制代码
function myFunction() {
    var localVar = "I'm a local variable"; // 在函数内部声明的变量是局部变量,只能在函数内部访问
    console.log(localVar); // 在函数内部可以访问局部变量
}

// console.log(localVar); // 这里会报错,因为无法在函数外部访问局部变量

myFunction(); // 调用函数

函数作用域指的是在函数内部声明的变量只能在该函数内部访问,外部代码无法访问函数内部的变量。

在这个例子中,localVar 是在 myFunction 函数内部声明的局部变量,它只能在函数内部被访问。如果尝试在函数外部访问 localVar,会导致错误。函数作用域有助于保护变量,避免命名冲突,并且提高了代码的可维护性和可读性。

动态作用域

scss 复制代码
// 动态作用域示例(伪代码)
function foo() {
    console.log(bar);
}

function baz() {
    var bar = "I'm from baz";
    foo();
}

function qux() {
    var bar = "I'm from qux";
    baz();
}

qux(); // 输出: "I'm from qux"

动态作用域(Dynamic Scope)是一种变量作用域的规则,与静态作用域相反,它的作用域在运行时根据调用堆栈动态确定,而不是在代码编写时确定。

在动态作用域中,一个函数的作用域是由它被调用的位置所决定的,而不是它被定义的位置。这意味着在函数内部访问的变量是根据调用链的最后一个调用确定的。动态作用域在函数内部查找变量时,会沿着调用链向上查找,直到找到第一个匹配的变量。

动态作用域在一些编程语言中存在,但在大多数编程语言中,包括 JavaScript,采用的是静态作用域。

在这个示例中,当 foo 函数内部访问 bar 变量时,它会沿着调用链向上查找,最终找到 qux 函数中的 bar 变量,因此输出的是 "I'm from qux"。这就是动态作用域的工作原理。

作用域闭包

javascript 复制代码
function outerFunction() {
    var outerVariable = "I'm from outer";

    // 内部函数引用了外部函数的变量
    function innerFunction() {
        console.log(outerVariable); // 内部函数可以访问外部函数的变量
    }

    // 返回内部函数,形成闭包
    return innerFunction;
}

var closure = outerFunction(); // 调用外部函数,返回内部函数
closure(); // 执行内部函数,输出 "I'm from outer"

当内部函数引用了外部函数的变量时,就创建了闭包。闭包使得函数能够访问定义时的作用域,即使在外部函数执行完毕后仍然可以访问这些变量。

在这个例子中,innerFunction 是在 outerFunction 内部定义的函数,并且内部函数引用了外部函数的变量 outerVariable。即使 outerFunction 执行完毕后,我们仍然可以通过 closure 变量来调用 innerFunction,并且 innerFunction 仍然可以访问到 outerVariable,这就是闭包的概念。闭包使得函数可以保留对它们所创建的词法环境的引用,从而可以访问外部作用域中的变量和参数。

动态作用域

scss 复制代码
// 模拟动态作用域
function dynamicScope() {
    console.log(bar); // 内部函数访问变量 bar,根据调用链查找
}

function foo() {
    var bar = "I'm from foo";
    dynamicScope(); // 在 foo 内部调用 dynamicScope
}

function baz() {
    var bar = "I'm from baz";
    foo(); // 在 baz 内部调用 foo
}

function qux() {
    var bar = "I'm from qux";
    baz(); // 在 qux 内部调用 baz
}

qux(); // 输出: "I'm from qux"

vavaScript 本身不支持动态作用域,但是可以通过模拟来演示动态作用域的概念。

在这个示例中,当 dynamicScope 函数内部访问 bar 变量时,它会沿着调用链向上查找,最终找到 qux 函数中的 bar 变量,因此输出的是 "I'm from qux"。虽然 JavaScript 本身不支持动态作用域,但这个示例通过模拟动态作用域来说明了这种工作原理。

结尾

以下是初入作用域的功能简介,还有更高质量的代码和解读详细的提示,后期长安会持续更新中...

相关推荐
罗_三金7 分钟前
前端框架对比和选择?
javascript·前端框架·vue·react·angular
Fan_web44 分钟前
JavaScript高级——闭包应用-自定义js模块
开发语言·前端·javascript·css·html
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
好名字08212 小时前
monorepo基础搭建教程(从0到1 pnpm+monorepo+vue)
前端·javascript
c#上位机2 小时前
C#事件的用法
java·javascript·c#
万物得其道者成2 小时前
React Zustand状态管理库的使用
开发语言·javascript·ecmascript
小白小白从不日白2 小时前
react hooks--useReducer
前端·javascript·react.js
下雪天的夏风3 小时前
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
前端·javascript·typescript
diygwcom3 小时前
electron-updater实现electron全量版本更新
前端·javascript·electron
volodyan3 小时前
electron react离线使用monaco-editor
javascript·react.js·electron