作用域链

  在某个作用域访问某个变量或者函数时,会首先在自己的局部环境作用域中搜寻变量或者函数,如果本地局部环境作用域中有该变量或者函数,则就直接使用找到的这个变量值或者函数;如果本地局部环境作用域中没有搜寻到要使用的变量或者函数名,那么就会在上一层的环境作用域中继续寻找,如果在上一层环境作用域中寻找到要使用的变量或者函数名,那么就使用上一层环境作用域中的变量或者函数名;否则,如果在上一层仍旧没有找到要使用的变量名或者函数名,就会继续找上一层的上一层作用域,以此类推,直到进入到全局环境中,如果都没有找到的话,那么就认为是undefined(未声明、未定义)。

  换句话说,如果在局部环境中没有找到变量或者函数,就在上一层寻找,知道找到为止,最终都没找到的话,就是undefined。

闭包

  先看下面一个例子:

  1. function demo(){
  2. var name="abc";
  3. }
  4. console.log(name);//undefined

  上面的代码很好理解,定义在demo函数内部的name变量,在外部是访问不到的。

  你可能会说,都没有调用demo(),怎么可能得到name变量的值,如你所愿:

  1. function demo(){
  2. var name="abc";
  3. }
  4. demo();
  5. console.log(name);//undefined

  事实上,即使调用了demo()函数之后,name变量在函数外部也是访问不到的。

  但是有很多种方法可以让外部可以访问name变量。

方法1:将name声明为全局变量

  1. function demo(){
  2. name="abc";
  3. }
  4. demo();
  5. console.log(name);//abc
  6. console.log(window.name);//abc

  name声明为全局变量之后,也就是成为window对象的一个属性而已。

  这个方法最好不要使用,不然会造成环境中太多的全局变量,一旦有重复声明,那么就会造成重复声明之前的那些变量值改变,导致错误。

方法2:将name变量在函数中返回

  1. function demo(){
  2. var name="abc";
  3. return name;
  4. }
  5. console.log(demo());

  这种方式很常用,因为很符合一般人的思维。

方法3:使用闭包

  1. function demo(){
  2. var name="abc";
  3. return function(){
  4. return name;
  5. }
  6. }
  7. var test = demo();
  8. console.log(test());//abc

  

闭包的概念

  网上有很多中关于闭包的说法,我觉得其中有一种特别好理解:

闭包就是在一个函数中又定义了一个函数,内部函数可以访问到外部函数作用域内的变量,此时,定义的这个内部函数就叫做闭包。

并且,无论内部函数在哪里调用,都能访问到外部函数作用域中的变量。

  看上面那个方法3中的demo函数,内部定义了一个函数,内部函数访问外部函数(demo)作用域中的name变量,内部函数称为闭包。

为什么要使用闭包

  1. function demo(){
  2. var name="abc";
  3. return name;
  4. }
  5. console.log(demo());

  就以上面的例子而言,如果想要获取demo函数中的name值,那么就需要调用一下demo函数,虽然在调用函数之后,可以获取内部的name值,但是函数调用结束,其中的name变量就已经在内存中被销毁了。

  闭包就可以解决这个问题,使用闭包的话,在调用外部函数之后,外部函数的变量并不会立即销毁,而会一直保存。所以使用闭包有可能会引起内存占用大的情况。

题外话

  请看下面的代码:

  1. var i = 10
    (function(x){
  2. console.log(x)
  3. })(i)
    //输出10

  上面的代码看着的确很别扭,可以简化一下:

  1. i = 10;
  2. var demo = function(x){
  3. console.log(x)
  4. }
  5. demo(i)

  没错,其实就是利用的匿名函数赋值给变量,然后利用变量加圆括号来调用函数。

  1. i = 10;
  2. (function(x){//x是定义的形参
  3. console.log(x)
  4. })(i) //i是传递给函数的实参

  

  可以看一下这种用法的示例:

  1. function demo(){
  2. var funcArr = new Array();//funArr是保存函数的数组
  3. for(var i=0;i<10;i++){
  4. funcArr[i] = function(){
  5. console.log(i) //每次调用函数的时候,打印i值
  6. }
  7. }
  8. return funcArr; //返回保存函数的数组
  9. }
  10. var func = demo();
  11. func[1]() //10
  12. func[2]() //10

  上面的代码并没有按照想象中运行,并没有func[1]()输出1,func[2]()输出2

  这是因为JavaScript没有块级作用域的原因,解决方法就可以利用上面的用法:

  1. function demo(){
  2. var funcArr = new Array();//funArr是保存函数的数组
  3. for(var i=0;i<10;i++){
  4. funcArr[i] = (function(x){
  5. return function(){
  6. console.log(x) //每次调用函数的时候,打印x值,x是形参,i才是实参
  7. }
  8. })(i)
  9. }
  10. return funcArr; //返回保存函数的数组
  11. }
  12. var func = demo();
  13. func[1]() //1
  14. func[2]() //2

  

  闭包使用最多的场景接收:保存环境变量,将返回的函数中要使用的某些现场变量,防止返回的函数的某些参数受到外界影响。

JavaScript 作用域链与闭包的更多相关文章

  1. 个人理解的javascript作用域链与闭包

    闭包引入的前提个人理解是为从外部读取局部变量,正常情况下,这是办不到的.简单的闭包举例如下: function f1(){ n=100; function f2(){ alert(n); } retu ...

  2. 深入javascript作用域链到闭包

    我之前用过闭包,用过this,虽然很多时候知道是这么一回事,但是确实理解上还不够深入.再一次看javascript高级程序设计这本书时,发现一起很多疑难问题竟然都懂了,所以总结一下一些理解,难免有错, ...

  3. javascript 作用域链及闭包,AO,VO,执行环境

    下面的文章内容会根据理解程度不断修正. js变量作用域: 定义:变量在它申明的函数体以及函数体内嵌套的任意函数体内有定义. function AA(){ var bb='我是AA内部变量'; func ...

  4. JavaScript作用域链与闭包的理解

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域 链的工作原理. 1. 全局作用域(Global Scope) (1)最外层函数和 ...

  5. 【进阶2-2期】JavaScript深入之从作用域链理解闭包(转)

    这是我在公众号(高级前端进阶)看到的文章,现在做笔记   https://github.com/yygmind/blog/issues/18 红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一 ...

  6. 《浏览器工作原理与实践》<10>作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?

    在上一篇文章中我们讲到了什么是作用域,以及 ES6 是如何通过变量环境和词法环境来同时支持变量提升和块级作用域,在最后我们也提到了如何通过词法环境和变量环境来查找变量,这其中就涉及到作用域链的概念. ...

  7. 在chrome开发者工具中观察函数调用栈、作用域链与闭包

    在chrome开发者工具中观察函数调用栈.作用域链与闭包 在chrome的开发者工具中,通过断点调试,我们能够非常方便的一步一步的观察JavaScript的执行过程,直观感知函数调用栈,作用域链,变量 ...

  8. 在chrome开发者工具中观察函数调用栈、作用域链、闭包

    在chrome的开发者工具中,通过断点调试,我们能够非常方便的一步一步的观察JavaScript的执行过程,直观感知函数调用栈,作用域链,变量对象,闭包,this等关键信息的变化.因此,断点调试对于快 ...

  9. JS详细图解作用域链与闭包

    JS详细图解作用域链与闭包 攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你 ...

随机推荐

  1. 使用requests模块post payload请求

    import json import requests import datetime postUrl = 'https://sellercentral.amazon.com/fba/profitab ...

  2. Qt 编程指南 2 Hello Designer

    2 Hello Designer  ①编辑窗口部件:为窗口拖拽控件(即部件),编辑控件布局等. ②编辑信号/槽:用户在图形界面的一些动作会触发窗口或控件的信号,信号对应的处理函数就是槽函数,这种模式实 ...

  3. WebSocket原理与实践(三)--解析数据帧

    WebSocket原理与实践(三)--解析数据帧 1-1 理解数据帧的含义:   在WebSocket协议中,数据是通过帧序列来传输的.为了数据安全原因,客户端必须掩码(mask)它发送到服务器的所有 ...

  4. python3.6.4源码安装

    该脚本适合centos6和ubuntu用,关于debian可以根据脚本中的范例自己添加 微笑哥与你每天进步一点点 #!/bin/bash##Check systemcheck_sys(){ local ...

  5. nginx安装升级及配置详解

    1.简介 2.安装配置 3.配置文件介绍 4.启动.停止.平滑重启.升级 一.Nginx简介 Nginx(engine x)是俄罗斯人Igor Sysoev编写的一款高性能的http和反向代理服务器. ...

  6. android 解决ScrollView中的子布局不能够填充整个ScrollView的情况。

    在开发中如果你的xml文件的跟布局是ScrollView,在ScrollView中无论你写什么样的布局,其默认情况下都是不能填充整个布局的.也就是说你的ScrollView中的子布局设置fill_pa ...

  7. Android Canvas saveLayerAlpha使用

    Canvas.saveLayerAlpha(float left, float top, float right, float bottom, int alpha, int saveFlags): 本 ...

  8. c语言程序设计 第一例子

    #include <studio.h> int main(){ printf("this is  dog.\n"); return 0; } studio.h 表示st ...

  9. select 下拉选中

    <body> <select name="" id=""> <option value="">张三< ...

  10. linux svn代码回滚命令

    取消对代码的修改分为两种情况: 第一种情况:改动没有被提交(commit). 这种情况下,使用svn revert就能取消之前的修改. svn revert用法如下: # svn revert [-R ...