返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Javascript的作用域、作用域链以及闭包详解
  • 165
分享到

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

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

一、javascript中的作用域 ①全局变量-函数体外部进行声明 ②局部变量-函数体内部进行声明 1)函数级作用域 javascript语言中局部变量不同于C#、Java等高级语言

一、javascript中的作用域

①全局变量-函数体外部进行声明

②局部变量-函数体内部进行声明

1)函数级作用域

javascript语言中局部变量不同于C#、Java等高级语言,在这些高级语言内部,采用的块级作用域中会声明新的变量,这些变量不会影响到外部作用域。

而javascript则采用的是函数级作用域,也就是说js创建作用域的单位是函数。

例如:

在C#当中我们写如下代码:

static void Main(string[] args)
{
     for (var x = 1; x < 10; x++)
     {
           Console.WriteLine(x.ToString());
     }
     Console.WriteLine(x.ToString());
}

上面代码会出现如下的编译错误:

The name 'x' does not exist in the current context

同样在javascript当中写如下代码:

<script>
    function main() {
        for (var x = 1; x < 10; x++) {
             console.log(x.toString());
         }
         console.log(x.toString());
     }
     main();
</script>

输出结果如下:

[WEB浏览器] "1"

[Web浏览器] "2"

[Web浏览器] "3"

[Web浏览器] "4"

[Web浏览器] "5"

[Web浏览器] "6"

[Web浏览器] "7"

[Web浏览器] "8"

[Web浏览器] "9"

[Web浏览器] "10"

这就说明了,“块级作用域”和“函数级作用域”的区别,块级作用域当离开作用域后,外部就不能用了,就像上面的C#例子,"x"离开for循环后就不能用了,但是javascript中不一样,它还可以进行访问。

2)变量提升

再看javascript中作用域的一个特性,例子如下:

function func(){
    console.log(x);
    var x = 1;
    console.log(x);
}        
func();

输出结果:

[Web浏览器] "undefined"

[Web浏览器] "1"

如上面的结果:有意思的是,为什么第一个输出是“undefined”呢?这就涉及到javascript中的“变量提升”,其实我感觉叫“声明提升”更好,因为这个机制就是把变量的声明提前到函数的前面。并不会把值也同样提升,也就是为什么第一次没有输出“1”的原因。

上面的代码就相当于:

function func(){
    var x;
    console.log(x);
    var x = 1;
    console.log(x);
}        
func();

3)函数内部用不用“var”对程序的影响

这是个值得注意的地方:

var x = 1;
function addVar(){
    var x = 2;
    console.log(x);            
}
addVar();
console.log(x);

输出:

[Web浏览器] "2"

[Web浏览器] "1"

当在函数内部去掉var之后,再执行:

var x = 1;
 function delVar(){
       x = 2;
       console.log(x);            
  }
 delVar();
 console.log(x);

[Web浏览器] "2"

[Web浏览器] "2"

上面的例子说明了,在函数内部如果在声明变量没有使用var,那么声明的变量就是全局变量。

二、javascript的作用域链

先看如下的代码:

var name="Global";
function t(){
        var name="t_Local";
        
        function s1(){
            var name="s1_Local";
                console.log(name);
        }
        function s2(){
                console.log(name);
        }
        s1();
        s2(); 
}
t();

输出结果:

[Web浏览器] "s1_Local"

[Web浏览器] "t_Local"

那么就有几个问题:

  • 1.为什么第二次输出的不是s1_Local?
  • 2.为什么不是Global?

解决这个两个问题就在于作用域链…

下面就解析一下这个过程,

例如当上面程序创建完成的时候,会形成上图中的链状结构(假想的),所以每次调用s1()函数的时候,console.log(name);先会在他自己的作用域中寻找name这个变量,这里它找到name=“s1_Local”,所以就输出了s1_Local,而每次调用s2()的时候,它和s1()函数过程一样,只不过在自身的作用域没有找到,所以向上层查找,在t()函数中找到了,于是就输出了"t_Local"。

同样如果我们可以验证一下,如果把t中的name删除,可以看看输出是不是“Global”

结果如下:

[Web浏览器] "s1_Local"

[Web浏览器] "Global"

当然具体每一个作用域直接是如何连接的,请参考

https://www.jb51.net/article/28610.htm

其中也说明了为什么JS当中应该尽量减少with关键字的使用。

三、闭包

了解了作用域和作用域链的概念之后,再去理解闭包就相对容易了。

1.闭包第一个作用

还是先看例子:

function s1() {
    var x = 123;
    return s2();
}

function s2() {
    return x;
}

alert(s1());

这里我想弹出x的值,但是却发生了错误 "Uncaught ReferenceError: x is not defined",根据作用域链的知识,了解到因为s2中没有x的定义,并且向上找全局也没有x定义,所以就报错了。也就是说s1和s2不能够共享x的值。

那么问题来了,我想要访问s1当中的x,怎么弄?

修改代码如下:

function s1() {
    var x = 123;
    return function(){
        return x;
    };
}
    
var test = s1();
console.log(test());

结果为:

[Web浏览器] "123"

解释:因为function本质也是数据,所以它和x的作用域相同,可以访问x。这样就相当于对外部开放了一个可以访问内部变量的接口。

还可以怎么玩,稍微修改一下代码

var func;
function f(){
var x='123';
func=function(){
    return x;
    };
}
f();
alert(func());

定义一个全局的变量,然后在函数内部让其等于一个函数,这样就可以通过这个全局变量来进行访问x了。

综上:闭包是啥?闭包就相当于函数外部和内部的桥梁,通过闭包可以在外部访问函数内部的变量。这也是闭包的第一个作用。

2.闭包的第二个作用

先看代码:

function f1(){
    var n=1;
    add=function(){
        n+=1;
    };
    function f2(){
        console.log(n);
        return '输出完成';
    }
    return f2;
}
var res=f1();
console.log(res());
add();
console.log(res());

输出结果:

[Web浏览器] "1"

[Web浏览器] "输出完成"

[Web浏览器] "2"

[Web浏览器] "输出完成"

问题为什么第二次输出的结果n变成了2,没有被清除?

我的理解:res是一个全局变量,一直驻留在内存当中,它就相当于f2函数,f2函数又要依赖于f1函数,所以f1也驻留在内存当中,保证不被GC所回收,每一次调用add函数的时候,就相当于把f1中的n重新赋值了,这样n的值就变化了。这也是闭包的第二个作用,可以让变量的值始终保存在内存当中。

3.闭包的应用

①可以做访问控制(相当于C#当中的属性)

//定义两个变量,用于存储取值和存值
var get,set; 
//定义一个自调用函数,设定set和get方法
(function(){
    //设定x的默认值
    var x = 0;
    set = function(n){
        x = n;
    }
    get = function(){
        return x;
    }
})();

console.log(get());
set(5);
console.log(get());

输出结果:

[Web浏览器] "0"

[Web浏览器] "5"

②可以用做迭代器

//定义一个函数,里面使用了闭包
function foo(myArray){
    var i=0;
    //闭包迭代器
    next=function(){
        //每次返回数组的当前值,下标+1
        return myArray[i++];
    }
}
//调用foo,参数为一个数组
foo(['a','b','c','d']);
//每次调用next都会打印数组的一个值
console.log(next());
console.log(next());
console.log(next());
console.log(next());

输出结果:

[Web浏览器] "a"

[Web浏览器] "b"

[Web浏览器] "c"

[Web浏览器] "d"

③闭包在循环中的使用

例1

function f(){
    var a=[];
    var i;
    for(i=0;i<3;i++){
        a[i]=function(){
            return i;
        };
    }
    return a;
}
var test=f();
console.log(test[0]());
console.log(test[1]());
console.log(test[2]());

输出结果:

[Web浏览器] "3"

[Web浏览器] "3"

[Web浏览器] "3"

为什么结果不是0、1、2?

这里我们使用了闭包,每次循环都指向了同一个局部变量i,但是闭包不会记录每一次循环的值,只保存了变量的引用地址,所以当我们在调用test[0]()、test[1]()、test[2]()的时候都返回的是for循环最后的值,也就是3的时候跳出了循环。

例2:我想输出0,1,2怎么搞?

function f(){
    var a=[];
    var i;
    for(i=0;i<3;i++){
        a[i]=(function(x){
            return function(){
                return x;
            }
        })(i);
    }
    return a;
}
var test=f();
console.log(test[0]());
console.log(test[1]());
console.log(test[2]());

结果:

[Web浏览器] "0"

[Web浏览器] "1"

[Web浏览器] "2"

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: Javascript的作用域、作用域链以及闭包详解

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

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

猜你喜欢
  • Javascript的作用域、作用域链以及闭包详解
    一、javascript中的作用域 ①全局变量-函数体外部进行声明 ②局部变量-函数体内部进行声明 1)函数级作用域 javascript语言中局部变量不同于C#、Java等高级语言...
    99+
    2024-04-02
  • javascript作用域和作用域链详解
    目录一、javascript的作用域1、全局作用域2、局部作用域二、javascript的作用域链三、作用域链和优化四、改变作用域链1、with语法改变作用域链2、catch语法总结...
    99+
    2024-04-02
  • Javascript之作用域、作用域链、闭包的示例分析
    这篇文章主要介绍Javascript之作用域、作用域链、闭包的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!什么是作用域?作用域是一种规则,在代码编译阶段就确定了,规定了变量...
    99+
    2024-04-02
  • JavaScript深入理解作用域链与闭包详情
    目录深入作用域链与闭包作用域链[[Environment]]完善环境记录闭包函数实例什么是闭包变量绑定同一个闭包总结深入作用域链与闭包 为什么要把作用域链和闭包放在一起讲呢,它们有什...
    99+
    2024-04-02
  • Javascript作用域与闭包详情
    目录1、作用域2、作用域链3、词法作用域5、闭包的应用6、闭包的缺陷7、高频闭包面试题1、作用域 简单来说,作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限 在E...
    99+
    2024-04-02
  • JavaScript闭包和作用域链的定义实现
    目录引言闭包的定义和实现作用域链闭包和作用域链的关系使用闭包的注意事项结论引言 在JavaScript中,每个函数都有自己的作用域。作用域规定了哪些变量和函数可以在当前函数内部访问。...
    99+
    2023-05-20
    JavaScript闭包作用域链 JavaScript作用域链
  • JavaScript 中的作用域与闭包
    目录一、JavaScript 是一门编译语言1.1 传统编译语言的编译步骤1.2 JavaScript 与传统编译语言的区别二、作用域(Scope)2.1 LHS查询 和 RHS查询...
    99+
    2024-04-02
  • JavaScript函数执行、作用域链以及内存管理详解
    目录前言函数执行全局执行上下文函数执行上下文作用域链内存管理引用计数标记清除前言 在我们平常编写JavaScript代码的时候,难免会用到函数,函数里面会有各种变量,这些变量的作用的...
    99+
    2023-01-08
    JS函数执行 js作用域链 js内存管理
  • JavaScript详细解析之作用域链
    以上就是JavaScript详细解析之作用域链的详细内容,更多请关注编程网其它相关文章!...
    99+
    2022-11-22
    JavaScript 前端
  • JavaScript(函数,作用域和闭包)
    目录 一,什么是函数1.1,常用系统函数1.2,函数声明 1.3,函数表达式二,预解析2.1,函数自调用 2.2,回调函数三,变量的作用域3.1,隐式全局变量 四,作用域与块级作用...
    99+
    2023-09-05
    前端 javascript 开发语言
  • Javascript的作用域和作用域链是什么
    这篇文章主要介绍Javascript的作用域和作用域链是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Javascript 的作用域和作用域链作用域: 作用域是定义变量...
    99+
    2024-04-02
  • js作用域及作用域链工作引擎
    目录前言一、作用域(scope)1.作用域的分类2.函数体作用域3.块级作用域二、预编译三、作用域链前言 我们需要先知道的是引擎,引擎的工作简单粗暴,就是负责javascript从头...
    99+
    2024-04-02
  • JavaScript作用域与作用域链使用重点讲解
    作用域和作用域链方面的知识是JS的重点,去面试十个有八个都会问你这块的知识,所以说这块是特别特别的重要,下面我们好好理解一下作用域和作用域链到底是个什么: 先上一段代码: var a...
    99+
    2022-11-13
    JS作用域与作用域链 JS作用域 JS作用域链
  • 详解JavaScript作用域
    这篇文章主要介绍“详解JavaScript作用域”,在日常操作中,相信很多人在详解JavaScript作用域问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”详解JavaScri...
    99+
    2024-04-02
  • JavaScript作用域和作用域链的示例分析
    这篇文章给大家分享的是有关JavaScript作用域和作用域链的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。作用域(Scope)1. 什么是作用域作用域是在运行时代码中...
    99+
    2024-04-02
  • JS作用域作用链及this使用原理详解
    目录变量提升的原理:JavaScript的执行顺序第一部分:变量提升部分的代码第二部分:代码执行部分代码执行阶段调用栈:栈溢出的原理如何利用调用栈1.使用浏览器查看调用栈的信息2.小...
    99+
    2022-11-13
    JS作用域作用链this JS作用域作用链
  • 如何分析javascript作用域和作用域链
    如何分析javascript作用域和作用域链,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、javascript的作用域1、全局作用域1、最外层函数和最外层函...
    99+
    2023-06-28
  • JavaScript作用域与作用域链优化方式
    目录前言内容作用域作用域的嵌套作用域的一些实现细节作用域链相关优化总结前言 作用域和作用域链是所有JavaScript开发人员每天都要接触和应用的内容。不管是面试中的作用域链的面试考...
    99+
    2024-04-02
  • JavaScript闭包原理及作用详解
    目录简介闭包的用途柯里化实现公有变量缓存封装(属性私有化)闭包的原理垃圾收集简介实际开发中的优化简介 说明 本文介绍JavaScript的闭包的作用、用途及其原理。 闭包的定义 闭包...
    99+
    2024-04-02
  • Golang函数的闭包特性和作用域链
    Golang函数中的作用域链和闭包特性,需要具体代码示例 一、函数的作用域链在Golang中,函数的作用域链是指函数中变量的访问权限范围。作用域链是一种层层嵌套的结构,每一层作用域都可以访问外层作用域的变量,...
    99+
    2024-01-18
    Golang 闭包 作用域链
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作