iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >在javascript中闭包是不是作用域
  • 390
分享到

在javascript中闭包是不是作用域

2024-04-02 19:04:59 390人浏览 独家记忆
摘要

本篇内容介绍了“在javascript中闭包是不是作用域”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

本篇内容介绍了“在javascript中闭包是不是作用域”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!


教程操作环境:windows7系统、javascript1.8.5版、Dell G3电脑。

我们知道,作用域链查找标识符的顺序是从当前作用域开始一级一级往上查找。因此,通过作用域链,JavaScript 函数内部可以读取函数外部的变量,但反过来,函数的外部通常则无法读取函数内部的变量。在实际应用中,有时需要在函数外部访问函数的局部变量,此时最常用的方法就是使用闭包。

闭包是 JavaScript 的重要特性之一,在函数式编程中有着重要的作用,本节介绍闭包的结构和基本用法。

那么什么是闭包?

闭包是一个能够持续存在的函数上下文活动对象,是同时含有对函数对象以及作用域对象引用的对象。闭包主要是用来获取作用域链或原型链上的变量或值。创建闭包最常用的方式是在一个函数中声明内部函数(也称嵌套函数),并返回内部函数。

此时在函数外部就可以通过调用函数得到内部函数,进而调用内部函数来实现对函数局部变量的访问。此时的内部函数就是一个闭包。虽然按照闭包的概念,所有访问了外部变量的 JavaScript 函数都是闭包,但我们平常绝大部分时候所谓的闭包其实指的就是内部函数闭包。

闭包可以将一些数据封装为私有属性以确保这些变量的安全访问,这个功能给应用带来了极大的好处。需要注意的是,闭包如果使用不当,也会带来一些意想不到的问题。下面就通过几个示例来演示一下闭包的创建、使用和可能存在的问题及其解决方法。

形成原理

函数被调用时,会产生一个临时上下文活动对象。它是函数作用域的顶级对象,作用域内所有私有方法有变量、参数、私有函数等都将作为上下文活动对象的属性而存在。

函数被调用后,在默认情况下上下文活动对象会被立即释放,避免占用系统资源。但是,若函数内的私有变量、参数、私有函数等被外界引用,则这个上下文活动对象暂时会继续存在,直到所有外界引用被注销。

但是,函数作用域是封闭的,外界无法访问。那么在什么情况下,外界可以访问到函数内的私有成员呢?

根据作用域链,内部函数可以访问外部函数的私有成员。如果内部函数引用了外部函数的私有成员,同时内部函数又被传给外界,或者对外界开放,那么闭包体就形成了。这个外部函数就是一个闭包体,它被调用后,活动对象暂时不会被注销,其属性会继续存在,通过内部函数可以持续读写外部函数的私有成员。

闭包结构

典型的闭包体是一个嵌套结构的函数。内部函数引用外部函数的私有成员,同时内部函数又被外界引用,当外部函数被调用后,就形成了闭包。这个函数也称为闭包函数。

下面是一个典型的闭包结构。

function f(x) {  //外部函数
    return function (y) {  //内部函数,通过返回内部函数,实现外部引用
        return x + y;  //访问外部函数的参数
    };
}
var c = f(5);  //调用外部函数,获取引用内部函数
console.log(c(6));  //调用内部函数,原外部函数的参数继续存在

解析过程简单描述如下:

  • 在 JavaScript 脚本预编译期,声明的函数 f 和变量 c,先被词法预解析。

  • 在 JavaScript 执行期,调用函数 f,并传入值 5。

  • 在解析函数 f 时,将创建执行环境(函数作用域)和活动对象,并把参数和私有变量、内部函数都映射为活动对象的属性。

  • 参数 x 的值为 5,映射到活动对象的 x 属性。

  • 内部函数通过作用域链引用了参数 x,但是还没有被执行。

  • 外部函数被调用后,返回内部函数,导致内部函数被外界变量 c 引用。

  • JavaScript 解析器检测到外部函数的活动对象的属性被外界引用,无法注销该活动对象,于是在内存中继续维持该对象的存在。

  • 当调用 c,即调用内部函数时,可以看到外部函数的参数 x 存储的值继续存在。这样就可以实现后续运算操作,返回 x+y=5=6=11。

如下结构形式也可以形成闭包:通过全局变量引用内部函数,实现内部函数对外开放。

var c;  //声明全局变量
function f(x) {  //外部函数
    c = function (y) {  //内部函数,通过向全局变量开放实现外部引用
        return x + y;  //访问外部函数的参数
    };
}
f(5);  //调用外部函数
console.log(c(6));  //使用全局变量c调用内部函数,返回11

闭包变体

除了嵌套函数外,如果外部引用函数内部的私有数组或对象,也容易形成闭包。

var add;  //全局变量
function f() {  //外部函数
    var a = [1,2,3];  //私有变量,引用型数组
    add = function (x) {  //测试函数,对外开放
        a[0] = x * x;  //修改私有数组的元素值
    }
    return a;  //返回私有数组的引用
}
var c = f();
console.log(c[0]);  //读取闭包内数组,返回1
add(5);  //测试修改数组
console.log(c[0]);  //读取闭包内数组,返回25
add(10);  //测试修改数组
console.log(c[0]);  //读取闭包内数组,返回100

与函数相同,对象和数组也是引用型数据。调用函数 f,返回私有数组 a 的引用,即传值给局部变量 c,而 a 是函数 f 的私有变量,当被调用后,活动对象继续存在,这样就形成了闭包。

这种特殊形式的闭包没有实际应用价值,因为其功能单一,只能作为一个静态的、单向的闭包。而闭包函数可以设计各种复杂的运算表达式,它是函数式变成的基础。

反之,如果返回的是一个简单的值,就无法形成闭包,值传递是直接复制。外部变量 c 得到的仅是一个值,而不是对函数内部变量的引用。这样当函数调用后,将直接注销对象。

function f(x) {  //外部函数
    var a = 1;  //私有变量
    return a;
}
var c = f(5);
console.log(c);  //仅是一个值,返回1

使用闭包

下面结合示例介绍闭包的简单使用,以加深对闭包的理解。

示例1

使用闭包实现优雅的打包,定义存储器。

var f = function () {  //外部函数
    var a = [];  //私有数组初始化
    return function (x) {  //返回内部函数
        a.push(x);  //添加元素
        return a;  //返回私有数组
    };
} ()  //直接调用函数,生成执行环境
var a = f(1);  //添加值
console.log(a);  //返回1
var b = f(2);  //添加值
console.log(b);  //返回1,2

在上面示例中,通过外部函数设计一个闭包,定义一个永久的存储器。当调用外部函数生成执行环境之后,就可以利用返回的匿名函数不断地的向闭包体内的数组 a 传入新值,传入的值会持续存在。

示例2

在网页中事件处理函数很容易形成闭包。

<script>
function f() {
    var a = 1;
    b = function () {
        console.log("a =" + a);
    }
    c = function () {
        a ++;
    }
    d = function () {
        a --;
    }
}
</script>
<button onclick="f()">生成闭包</button>
<button onclick="b()">查看 a 的值</button>
<button onclick="c()">递增</button>
<button onclick="d()">递减</button>

在浏览器中浏览时,首先点击“生成闭包”按钮,生成一个闭包;点击“查看 a 的值”按钮,可以随时查看闭包内私有变量 a 的值;点击“递增”“递减”按钮时,可以动态修改闭包内变量 a 的值,效果如图所示。

在javascript中闭包是不是作用域

闭包的局限性

闭包的价值是方便在表达式运算过程中存储数据。但是,它的缺点也不容忽视。

  • 由于函数调用后,无法注销调用对象,会占用系统资源,在脚本中大量使用闭包,容易导致内存泄漏。解决方法:慎用闭包,不要滥用。

  • 由于闭包的作用,其保存的值是动态,如果处理不当容易出现异常或错误。

示例

设计一个简单的选项卡效果。html 结构如下:

<div class="tab_wrap">
    <ul class="tab" id="tab">
        <li id="tab_1" class="hover">Tab1</li>
        <li id="tab_2" class="nORMal">Tab2</li>
        <li id="tab_3" class="normal">Tab3</li>
    </ul>
    <div class="content" id="content">
        <div id="content_1" class="show"><img scr="image/1.jpg" height="200" /></div>
        <div id="content_2" class="show"><img scr="image/2.jpg" height="200" /></div>
        <div id="content_3" class="show"><img scr="image/3.jpg" height="200" /></div>
    </div>
</div>

下面请看 JavaScript 脚本。

window.onload = function () {
    var tab = document.getElementById("tab").getElementsByTagName("li"),
        content = document.getElementById("content").getElementByTagName("div");
    for (var i = 0; i < tab.length;i ++) {
        tab[i].addEventListener("mouseover"), function () {
            for (var n = 0; n < tab.length; n ++) {
                tab[n].className = "normal";
                content[n].className = "none";
            }
            tab[i].className = "hover";
            content[i].className = "show";
        });
    }
}

在 load 事件处理函数中,使用 for 语句为每个 li 属性元素绑定 mouseover 事件;在 mouseover 事件处理函数中重置所有选项卡 li 的类样式,然后设置当前 li 选项卡高亮显示,同时显示对应的内容容器

但是在浏览器中预览时,会发现浏览器抛出异常。

SCRIPT5007:无法设置未定义或 null 引用的属性"className"

在 mouseover 事件处理函数中跟踪变量 i 的值,i 的值都变为了 3,tab[3] 自然是一个 null,所以也不能够读取 className 属性。

【原因分析】

上面 JavaScript 代码是一个典型的嵌套函数结构。外部函数为 load 事件处理函数,内部函数为 mouseover 事件处理函数,变量 i 为外部函数的私有变量。

通过事件绑定,mouseover 事件处理函数被外界引用(li 元素),这样就形成了一个闭包体。虽然在 for 语句中为每个选项卡 li 分别绑定事件处理函数,但是这个操作是动态的,因此 tab[i] 中 i 的值也是动态的,所以就出现了上述异常。

【解决方法】

解决闭包的缺陷,最简单的方法是阻断内部函数对外部函数的变量引用,这样就形成了闭包体。针对本示例,我们可以在内部函数(mouseover 事件处理函数)外边增加一层防火墙,不让其直接引用外部变量。

window.load = function () {
    var tab = document.getElementById("tab").getElementsByTagName("li"),
        content = document.getElementById("content").getElementsByTagName("div");
    for (var i = 0; i < tab.length; i ++ ) {
        (function (j) {
            tab[j].addEventListener("number", function () {
                for (var n = 0; n < tab.length; n ++) {
                    tab[n].className = "normal";
                    content[n].className = "none";
                }
                tab[j].className = "hover";
                conteng[j].className = "show";
            });
        }) (i);
    }
}

在 for 语句中,直接调用匿名函数,把外部函数的 i 变量传给调用函数,在调用函数中接收这个值,而不是引用外部变量 i,规避了闭包体带来的困惑。

“在javascript中闭包是不是作用域”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: 在javascript中闭包是不是作用域

本文链接: https://www.lsjlt.com/news/80469.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 在javascript中闭包是不是作用域
    本篇内容介绍了“在javascript中闭包是不是作用域”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2022-10-19
  • JavaScript 中的作用域与闭包
    目录一、JavaScript 是一门编译语言1.1 传统编译语言的编译步骤1.2 JavaScript 与传统编译语言的区别二、作用域(Scope)2.1 LHS查询 和 RHS查询...
    99+
    2022-11-13
  • Javascript作用域与闭包详情
    目录1、作用域2、作用域链3、词法作用域5、闭包的应用6、闭包的缺陷7、高频闭包面试题1、作用域 简单来说,作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限 在E...
    99+
    2022-11-12
  • JavaScript(函数,作用域和闭包)
    目录 一,什么是函数1.1,常用系统函数1.2,函数声明 1.3,函数表达式二,预解析2.1,函数自调用 2.2,回调函数三,变量的作用域3.1,隐式全局变量 四,作用域与块级作用...
    99+
    2023-09-05
    前端 javascript 开发语言
  • Javascript闭包的作用是什么
    本篇内容主要讲解“Javascript闭包的作用是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Javascript闭包的作用是什么”吧!对于初学者来说,理...
    99+
    2022-10-19
  • Javascript的作用域、作用域链以及闭包详解
    一、javascript中的作用域 ①全局变量-函数体外部进行声明 ②局部变量-函数体内部进行声明 1)函数级作用域 javascript语言中局部变量不同于C#、Java等高级语言...
    99+
    2022-11-13
  • 在JavaScript函数中什么是闭包
    本篇内容介绍了“在JavaScript函数中什么是闭包”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!匿名函...
    99+
    2022-10-19
  • Javascript之作用域、作用域链、闭包的示例分析
    这篇文章主要介绍Javascript之作用域、作用域链、闭包的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!什么是作用域?作用域是一种规则,在代码编译阶段就确定了,规定了变量...
    99+
    2022-10-19
  • JavaScript中什么是闭包
    这篇文章给大家介绍JavaScript中什么是闭包,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。 一 、词法定义域 LexicalClosure闭包是编程语言Lexical ...
    99+
    2022-10-19
  • JavaScript的闭包与变量作用域介绍
    这篇文章主要讲解了“JavaScript的闭包与变量作用域介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript的闭包与变量作用域介绍”吧!...
    99+
    2022-10-19
  • JavaScript闭包和作用域链的定义实现
    目录引言闭包的定义和实现作用域链闭包和作用域链的关系使用闭包的注意事项结论引言 在JavaScript中,每个函数都有自己的作用域。作用域规定了哪些变量和函数可以在当前函数内部访问。...
    99+
    2023-05-20
    JavaScript闭包作用域链 JavaScript作用域链
  • JavaScript中闭包的概念、原理及作用是什么
    本篇内容主要讲解“JavaScript中闭包的概念、原理及作用是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript中闭包的概念、原理及作用是...
    99+
    2022-10-19
  • javascript中什么是作用域
    这篇文章给大家介绍javascript中什么是作用域,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。JavaScript可以做什么1.可以使网页具有交互性,例如响应用户点击,给用户提供更好的体验。2.可以处理表单,检验用...
    99+
    2023-06-14
  • javascript中什么指的是闭包
    这篇文章主要为大家展示了“javascript中什么指的是闭包”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“javascript中什么指的是闭包”这篇文章吧。 ...
    99+
    2022-10-19
  • JavaScript中的闭包是怎样的
    这篇文章给大家介绍JavaScript中的闭包是怎样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1. 闭包的概念来看一般函数的执行和启发:function stop() {  ...
    99+
    2023-06-22
  • 你真的了解JavaScript的作用域与闭包吗
    目录一、作用域二、闭包总结一、作用域 1.作用域总体来说就是根据名称查找变量的一套规则。JS查找变量的方式有两种:LHS和RHS。 LHS(left hand side)大致可以理解...
    99+
    2022-11-13
  • JavaScript深入理解作用域链与闭包详情
    目录深入作用域链与闭包作用域链[[Environment]]完善环境记录闭包函数实例什么是闭包变量绑定同一个闭包总结深入作用域链与闭包 为什么要把作用域链和闭包放在一起讲呢,它们有什...
    99+
    2022-11-13
  • javascript中闭包有什么作用
    本篇内容介绍了“javascript中闭包有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是闭...
    99+
    2022-10-19
  • JavaScript 函数闭包的含义和作用是什么
    这篇文章主要讲解了“JavaScript 函数闭包的含义和作用是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript 函数闭包的含义和作用...
    99+
    2022-10-19
  • 什么是JavaScript作用域
    本篇内容介绍了“什么是JavaScript作用域”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在JavaS...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作