Javascript的作用域、作用域链以及闭包详解

Javascript的作用域、作用域链以及闭包详解

什么是作用域?

作用域是指代码中定义变量的区域,也是访问这些变量的规则。在Javascript中常见的作用域有全局作用域和函数作用域。

全局作用域

全局作用域是指定义在最外层的变量,在整个程序执行过程中都可以访问到。例如下面的代码:

var name = "Lucy";

function getName() {
    console.log(name);
}

getName();       // 输出 "Lucy"

name 变量是在全局作用域中定义的。在 getName() 函数中,我们可以直接访问到 name 变量,并将其输出。

函数作用域

函数作用域是指在函数内部定义的变量,只能在函数内部被访问。例如下面的代码:

function sayHello() {
    var message = "Hello, world!";
    console.log(message);
}

sayHello();       // 输出 "Hello, world!"

console.log(message);   // 报错,message未定义

message 变量是在 sayHello() 函数内部定义的。在函数外部访问 message 变量会报错,因为它只存在于函数作用域中。

作用域链

当Javascript代码运行时,变量的查找会按照作用域链的顺序进行。作用域链是多个作用域的集合,用于查找变量。

作用域链的顺序

在Javascript中,当代码运行到某个作用域时,会按照以下顺序查找变量:

  1. 查找当前作用域下是否有该变量。如果当前作用域中没有该变量则继续往外查找。
  2. 查找上一层作用域下是否有该变量。如果上一层作用域中没有该变量则继续往外查找。
  3. 直到找到全局作用域,如果全局作用域中还没有该变量,则返回 undefined。

例如下面的代码:

var name = "Lucy";

function getName() {
    var message = "My name is " + name;
    console.log(message);
}

getName();       // 输出 "My name is Lucy"

getName() 函数中,我们访问了全局作用域中的 name 变量。当查找 name 变量时,会首先在函数作用域中查找,发现没有该变量,则往上一层作用域(即全局作用域)查找,最终找到了 name 变量。

作用域链的生命周期

当函数执行完成时,其作用域链会被销毁,其中的变量也会随之被销毁。例如下面的代码:

function sayHello() {
    var name = "Lucy";
    console.log("Hello, " + name + "!");
}

sayHello();       // 输出 "Hello, Lucy!"
console.log(name);   // 报错,name未定义

sayHello() 函数中,我们定义了 name 变量,当函数执行完成后,该变量会被销毁。在函数外部访问 name 变量会报错。

什么是闭包?

闭包是指在函数内部创建一个新的作用域,使得函数内部的变量可以被函数外部访问。通过闭包,我们可以实现变量的隐藏和封装,同时增强函数的灵活性。

创建闭包

在Javascript中,创建闭包有两种方式:

1. 返回函数

通过在函数内部定义一个新的函数,并返回该函数,从而使用闭包。例如下面的代码:

function createCounter() {
    var count = 0;

    function counter() {
        count++;
        console.log(count);
    }

    return counter;
}

var counterA = createCounter();   // counterA 是一个闭包
counterA();       // 输出 1
counterA();       // 输出 2

var counterB = createCounter();   // counterB 是另一个闭包
counterB();       // 输出 1

createCounter() 函数中,我们定义了 count 变量和 counter() 函数,并返回了 counter() 函数。当我们调用 createCounter() 函数时,实际上是在创建一个闭包。闭包中包含了 count 变量及其对应的值,以及 counter() 函数的引用。

2. 函数内声明函数

通过在函数内部声明一个新的函数,从而使用闭包。例如下面的代码:

function sayHello(name) {
    function hello() {
        console.log("Hello, " + name + "!");
    }

    hello();
}

sayHello("Lucy");     // 输出 "Hello, Lucy!"

sayHello() 函数中,我们定义了 hello() 函数,并在函数内部执行了 hello() 函数。由于 hello() 函数是在 sayHello() 函数内部定义的,因此可以访问 sayHello() 函数中的 name 参数。

示例说明

示例1:作用域链查询变量

var name = "Lucy";

function hehe() {
    var name = "Jack";

    function haha() {
        console.log(name);
    }

    haha();
}

hehe();       // 输出 "Jack"

hehe() 函数中,定义了 name 变量并赋值 "Jack"。在 haha() 函数中,访问了 name 变量。由于 name 变量在函数作用域中已经定义,因此查找时会先在函数作用域中查找,找到了name变量的值"Jack"。

示例2:闭包应用

function createFullName(firstName) {
    return function(lastName) {
        console.log(firstName + " " + lastName);
    };
}

var fullNameA = createFullName("Lucy");   // fullNameA 是一个闭包,firstName = "Lucy"
fullNameA("Smith");       // 输出 "Lucy Smith"
fullNameA("Brown");       // 输出 "Lucy Brown"

var fullNameB = createFullName("Jack");   // fullNameB 是另一个闭包,firstName = "Jack"
fullNameB("Johnson");     // 输出 "Jack Johnson"

createFullName() 函数中,我们返回了一个匿名函数,并使用了参数 firstName。当我们调用 createFullName() 函数时,实际上是在创建一个闭包。闭包中包含了 firstName 参数及其对应的值,以及匿名函数的引用。

当我们调用 fullNameA("Smith") 时,实际上是在执行闭包中的匿名函数,并传入了参数 lastName = "Smith"。在匿名函数中,会将 firstNamelastName 拼接起来,并输出结果。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Javascript的作用域、作用域链以及闭包详解 - Python技术站

(0)
上一篇 2023年6月7日
下一篇 2023年6月7日

相关文章

  • .NET使用一行命令轻松生成EF Core项目框架

    dotnet ef是Entity Framework Core(EF Core)的一个命令行工具,用于管理EF Core应用程序的数据库和代码。除了提供管理数据库的命令之外,dotnet ef还可以生成和管理实体和上下文代码。本文将介绍如何使用dotnet ef动态生成代码。 一、环境准备 1、项目准备 用vs2022新建一个.NET6的asp.net co…

    C# 2023年4月27日
    00
  • C#编写游戏客户端的实现代码

    C#编写游戏客户端的实现代码攻略 前言 C#是一种现代化的高级编程语言,它具有简洁的语法、面向对象的特性和丰富的类库,适用于各种不同类型的游戏开发。本攻略介绍了如何使用C#编写游戏客户端的实现代码,包含了从连接服务器到收发数据的完整过程。 步骤 1. 连接服务器 使用C#编写游戏客户端的第一步是连接到游戏服务器。可以使用System.Net.Sockets命…

    C# 2023年5月15日
    00
  • C#实现文件压缩与解压的方法示例【ZIP格式】

    C#实现文件压缩与解压的方法示例【ZIP格式】 简介 文件压缩与解压是常见的电子数据处理方式之一。其中,ZIP格式是比较常见的压缩文件格式。本文将介绍如何使用C#实现ZIP格式文件的压缩和解压操作,以及实例说明。 实现ZIP格式文件压缩 引用命名空间 首先需要引用System.IO.Compression命名空间。 using System.IO.Compr…

    C# 2023年6月1日
    00
  • ASP.NET Core实现动态获取文件并下载

    针对ASP.NET Core实现动态获取文件并下载的完整攻略,我们可以采用以下步骤: 第一步:设置下载文件的路由 在ASP.NET Core项目中,我们需要设置下载文件的路由,以便于我们通过对应的URL来访问和获取指定的文件。其中,我们建议采用FileResult类来指定文件的路径和MIME类型,示例代码如下: public virtual IActionR…

    C# 2023年6月3日
    00
  • C#实体对象序列化成Json并让字段的首字母小写的两种解决方法

    将C#实体对象序列化成Json并将字段的首字母小写有两种解决方法,一种是通过在类定义中使用DataMember属性,另一种是通过创建一个包含转换规则的自定义JsonConverter类。 1. 使用DataMember属性 我们可以在类定义中使用[DataContract]和[DataMember]特性,这样在序列化时会自动将所有被标记的属性转为小写。 us…

    C# 2023年5月31日
    00
  • PHP中配置IIS7实现基本身份验证的方法

    在PHP中配置IIS7实现基本身份验证的方法如下: 打开IIS管理器,选择要配置身份验证的站点,右键选择“编辑权限”。 在“安全”选项卡中,单击“编辑”按钮,打开“权限”对话框。 在“权限”对话框中,单击“添加”按钮,添加一个新的用户或组。 在“选择用户、计算机、服务帐户或组”对话框中,输入要添加的用户或组的名称,单击“检查名称”按钮,确保名称正确。 单击“…

    C# 2023年5月15日
    00
  • C#生成随机字符串的实例

    讲解“C#生成随机字符串的实例”的完整攻略包含以下几个步骤: 引入命名空间:首先需要引入 System.Security.Cryptography 命名空间,这个命名空间提供了 RNGCryptoServiceProvider 类,可以生成真正的随机数。 创建 RNGCryptoServiceProvider 类对象:创建一个 RNGCryptoServic…

    C# 2023年6月7日
    00
  • 新Orcas语言特性-查询句法

    下面我来为您详细讲解“新Orcas语言特性-查询句法”的完整攻略。 1. 什么是“查询句法”? Orcas是一门面向对象的编程语言,其中的“查询句法”是一种用于从集合中筛选出符合条件的元素的语法。使用“查询句法”,您可以用简单易懂的语言编写出复杂的查询语句,来实现类似于SQL的数据查询功能。 2. 如何使用“查询句法”? 要使用“查询句法”,您需要先创建一个…

    C# 2023年6月7日
    00
合作推广
合作推广
分享本页
返回顶部