资讯专栏INFORMATION COLUMN

老生常谈—Javascript作用域、变量提升、闭包

zhiwei / 3011人阅读

摘要:局部变量在函数中声明的变量,函数的参数作用域是局部性的,在函数体外,或者说的当前作用域的上层是无法直接读取的。执行结果这样我们就在外层取得了函数内部局部变量的也就是闭包实现从外部读取局部变量的能力。

浅谈作用域

  当我们新建一个可以储存变量的值,怎么才能读取到这个变量呢?能访问到这个变量,就说明符合作用域规则,作用域规则就可以说是js引擎读取变量的规则。
  在js中变量分为两种,全局变量和局部变量,全局变量(拥有全局作用域)可以在整个js应用中被所有代码访问到,从程序开始分配内存直至结束才会被释放(出于对代码的性能、可读性、以及潜在冲突考虑应该减少使用)。
  局部变量在函数中声明的变量,函数的参数(作用域是局部性的),在函数体外,或者说的当前作用域的上层是无法直接读取的。
  下面再说下作用域嵌套,产生作用域链的事,在我们实际编写代码的时候,比如:

var a = "a";
function demo(b) {
  let c = "c";
  function print(c) {
    console.log(a + " " + b + " " + c);
  }
  print(c);
}
demo("b");

执行结果:

  在print方法执行的时候,会从当前的执行作用域开始查找变量,如果找不到,就继续向上一级继续查找,如果找到则会停止,如果没有就会继续直至当最后到达全局作用域时,此时无论找到还是没找到,都会结束查找。

st=>start: 当前作用域 
io1=>start: 上层作用域
io2=>start: ...
cond=>end: 全局作用域

st->io1->io2->cond 

  (简单写了个流程图方便脑补

  接下来还是这段代码让我们来做一个小小的修改。

var a = "a";
function demo(b) {
  let c = "c",
      a = "d";
  function print(c) {
    console.log(a + " " + b + " " + c);
  }
  print(c);
}
demo("b");

执行结果:

  结果是输出了上一层的变量a,这也就是作用域查找的机制,作用域始终从运行时所处的最内部作用域开始,逐渐向外层查找,当遇到第一个符合条件的结果就会返回,停止查找。

变量提升

  先简单上一段代码

  a = "a";
  var a;
  console.log(a);

执行结果:
  为什么会有这种结果呢?声明在后,反而打印出前面声明得值,其实js在编译的时候会先进行声明操作,之后再进行赋值操作,也就是只要是在作用域中的声明一出现,就将在代码本身被执行前优先进行处理。也可以将这个过程理解为所有的声明(变量和函数)都会被“提升”到作用域的最顶端。(虽然函数声明和变量声明都会被提升,但是函数优先级要高于变量

在ES6引入了let和const可以用来声明创建块作用域。for循环头部定义强烈建议用let,感兴趣的人可以去看看js函数作用域和块作用域

闭包

  提到闭包很多人脑袋里都会感觉模模糊糊的,闭包在概念上定义是能够读取其他函数内部变量的函数,在中,只有函数内部的子函数才能读取这个函数局部变量,这时候前面说的作用域起到作用了,所以我们现在可以这样理解,定义在一个函数内部的函数,这个函数可以访问其他函数的变量,就可以称为“闭包”
  下面来聊聊闭包的用处,主要就是。1.能够读取到函数内部的变量,2.可以让变量始终保存在内存中(这点要注意后面解释),下面上代码。

function demo() { 
  let a = "a";
  function print() { 
    console.log(a);
  }
  return print; 
}
var clo = demo();
clo();

执行结果:
  这样我们就在外层取得了demo函数内部局部变量的a,也就是闭包实现从外部读取局部变量的能力。
  下面说一说让变量始终保存在内存中这点.

function demo() { 
  let a = 1;
  addA = () => a+=1;
  function print() { 
    console.log(a);
  }
  return print; 
}
var clo = demo();
clo();
addA();
clo();

执行结果:
  闭包可以阻止,demo内部作用域都被垃圾清理机制回收销毁,可以看到局部变量a第二次执行比第一次+1,这就说明第一次运行结束后啊,没有被回收,因为print()仍在使用这个作用域所以demo不会被回收。
闭包会使得对应的变量一直保存在内存中,不会被清理回收,对内存消耗大,别滥用

闭包在实际开发中的使用

  1.原生的setTimeout传递的函数不能带参数

//会报错
function func(param){
  console.log(param);
}
var test = func(1);
setTimeout(test, 1000);


//通过闭包实现传参效果
function func(param){
  return function(){
    console.log(param);
  }
}

var test = func(1);
setTimeout(test, 1000);

  2.封装变量

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102721.html

相关文章

  • 关于let的随想

    摘要:块状作用域提起中的关键字,第一个想法就是块状作用域。而如果通过这些关键词声明的,那么也就会声明到所在的作用域中。终于回到可以将变量绑定到所在的任意作用域中,通常是内部。避免不必要的出现。 读,想到哪里写到哪里。。。 块状作用域 提起ES6中的let关键字,第一个想法就是块状作用域。 说到作用域,以前提及的都是全局作用域和函数作用域。当进行作用域查找的时候,永远是一层一层往上查找,直到找...

    zhangwang 评论0 收藏0
  • Javascript作用变量提升

    摘要:有何区别在中,存在关键字,它声明的变量同样存在块级作用域。而且函数本身的作用域,只存在其所在的块级作用域之内,例如重复声明一次函数上面这段代码在中的输出结果为因为被条件语句中的上升覆盖了。如果对的使用,或的其他新特性感兴趣,请自行阅读文档。 引子 首先大家看一下下面的代码,猜猜会输出什么结果? var foo = 1; function bar() { if (!foo) { ...

    whlong 评论0 收藏0
  • Effective JavaScript读书笔记(二)

    摘要:尽可能的使用局部变量,少用全局变量。正确的实现就是在函数体内部使用将声明成局部变量。在新特性中,引入了块级作用域这个概念,因此还可以使用,来声明局部变量。它们共享外部变量,并且闭包还可以更新的值。 变量作用域 作用域,对于JavaScript语言来说无处不在,变量作用域,函数作用域(运行时上下文和定义时上下文),作用域污染等等都跟作用域息息相关,掌握JavaScript作用于规则,可以...

    Yuqi 评论0 收藏0
  • 老生常谈闭包(你不可不知的若干知识点)

    摘要:闭包是什么这是一个在面试的过程中出现的概率为以上的问题,也是我们张口就来的问题。文章推荐我们面试中在被问到闭包这个问题是要注意的几点闭包的延伸,让面试变得 闭包是什么?这是一个在面试的过程中出现的概率为60%以上的问题,也是我们张口就来的问题。但是我们往往发现,在面试的过程中我们的回答并不那么让面试官满意,我们虽然能张口说出一些但是却不能系统的对这个问题进行回答。面试官希望加入自己团队...

    daydream 评论0 收藏0
  • Node.js内存管理和V8垃圾回收机制

    摘要:垃圾回收内存管理实践先通过一个来看看在中进行垃圾回收的过程是怎样的内存泄漏识别在环境里提供了方法用来查看当前进程内存使用情况,单位为字节中保存的进程占用的内存部分,包括代码本身栈堆。 showImg(https://segmentfault.com/img/remote/1460000019894672?w=640&h=426);作者 | 五月君Node.js 技术栈 | https:...

    JowayYoung 评论0 收藏0

发表评论

0条评论

zhiwei

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<