JavaScript的作用域与作用域链||闭包以及为什么要使用闭包||对this的理解

1、JS高级程序设计关于这些个问题的解释:

1、执行环境及作用域:

执行环境(execution context,也有称之为执行上下文)是JS语言中最重要的一个概念。

Javascript没有代码块作用域的概念,局部作用域是针对函数来说的。

执行环境定义了变量或者函数有权访问到的其他数据,决定了他们的各自行为。

每个执行环境都有一个与之关联的变量对象(variable object),执行环境中定义的所有变量和函数都保存在这个对象中。无法访问,但是解析器在后台执行中要用到。

全局执行环境是最外围的执行环境。根据ECMAScript实现的宿主不同,表示执行环境的对象也不一样。Web浏览器中,全局执行环境是window对象,因此全局函数和全局变量都是作为window的对象和方法。

某个执行环境中的所有代码执行完毕后将被销毁,全局执行环境在关闭网页的时候才会被销毁。

每个函数都有自己的执行环境。当执行流进入函数的时候,函数的环境被推入到一个环境栈中。在函数执行以后,栈将其环境弹出,把控制权交给之前的执行环境。

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)

作用域链的作用是:是保证对执行环境有权访问的所有变量和函数的有序访问。作用域的前端始终是当前执行的代码所在的环境变量的变量对象。如果这个环境是一个函数,则将其活动对象(activation object)作为变量对象。作用域链中的下一个变量对象来自于包含环境。全局执行环境始终是作用域链中最后一个对象。

标识符解析始终是沿着作用域链一级一级地搜索标识符的过程,前端开始,逐级向上,直到找到标识符为止,找不到就会报错。

函数的作用域链包含着两个对象:它自己的变量对象(其中定义了arguments),和全局环境的变量对象。 

js作用域分函数作用域和全局作用域。

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。

作用域链的作用是:是保证对执行环境有权访问的所有变量和函数的有序访问。作用域的前端始终是当前执行的代码所在的环境变量的变量对象。如果这个环境是一个函数,则将其活动对象(activation object)作为变量对象。作用域链中的下一个变量对象来自于包含环境。全局执行环境始终是作用域链中最后一个对象。

标识符解析始终是沿着作用域链一级一级地搜索标识符的过程,前端开始,逐级向上,直到找到标识符为止,找不到就会报错。

函数的作用域链包含着两个对象:它自己的变量对象(其中定义了arguments),和全局环境的变量对象。

JavaScript的作用域与作用域链||闭包以及为什么要使用闭包||对this的理解_第1张图片

JavaScript的作用域与作用域链||闭包以及为什么要使用闭包||对this的理解_第2张图片

2、延长作用域链:

虽然js中的执行环境总共就两种:全局和函数(局部),但是还是有其他的办法来延长作用域链。就是在作用域的前端添加一个临时增加的变量对象。

具体使用:try...catch语句块的catch块;和with语句。

对于with语句来说,会将指定的对象添加到作用域链中。

function buildUrl(){
      var qs = "?debug=ture";
      with(location){
            var url = href + qs;
      }
      return url; 
}

3、没有块级作用域:

js中没有块级作用域,只有全局作用域和函数作用域。注意在if语句||for语句中定义的变量会被添加到全局变量中。

可以使用自执行语句来模拟块级作用域。

JavaScript的作用域与作用域链||闭包以及为什么要使用闭包||对this的理解_第3张图片

使用var声明的变量会被添加到最接近的环境中,函数内部就是函数作用域。如果初始化没有使用var,就会被添加到全局作用域中。

4、查询标识符:

 沿着作用域链向上查找。 

闭包以及为什么要使用闭包:

闭包与匿名函数不要搞混。

闭包:就是指有权访问另一个函数作用域中的变量的函数。创建闭包最常见的方式,就是在一个函数作用域内创建另一个函数。并且返回另一个函数。

需要闭包的原因:当使用函数被调用时,会创建一个执行环境(上下文环境)以及相应的作用域链,使用arguments和其他命名参数的值来初始化函数的活动对象(activation object)。函数执行过程就需要在作用域链中查找变量,查找过程不能向下,所以就需要闭包来读取函数内部的变量。闭包会让外层函数的变量一直存在于内存中。直到都不调用为止。

外层函数的变量对象(活动对象)存在于内层函数的作用域链中。

闭包的问题:

1、过度使用闭包会造成内存占用过多。

2、作用域链的这种配置引出一个问题,就是闭包只能取得包含函数中任何变量的最后一个值,闭包保存的是整个变量对象,而不是某个特殊的变量。

JavaScript的作用域与作用域链||闭包以及为什么要使用闭包||对this的理解_第4张图片

 

这里打印的0~9是console.log打印的。最终保存的结果i都是10。

以上函数,在for循环中使用闭包,这样最后读取到的i是最后一个值。存在风险。改用如下的方式,将当前值保存下来,用一个自执行的闭包函数来保存变量,之后结果传出。

JavaScript的作用域与作用域链||闭包以及为什么要使用闭包||对this的理解_第5张图片

js权威指南解释作为补充:

函数的执行依赖于变量作用域,这个作用域在函数定义的时候决定的,不是在函数调用的时候决定的。

古老解释对于闭包:函数对象通过作用域链来相互关联,函数体内的变量都可以保存在函数作用域内。

闭包可以捕捉到局部变量(和参数),并一直保存下来。

闭包的应用场景以及优缺点:

应用场景:1、闭包的典型框架应该就是jquery了。

     2、闭包是javascript语言的一大特点,主要应用闭包场合主要是为了:设计私有的方法和变量(模块设计,自执行闭包)。

     3、这在做框架的时候体现更明显,有些方法和属性只是运算逻辑过程中的使用的,不想让外部修改这些属性,因此就可以设计一个闭包来只提供方法获取。

优点:1. 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。

   2. 方便调用上下文的局部变量

         3. 加强封装性,第2点的延伸,可以达到对变量的保护作用。

缺点:闭包有一个非常严重的问题,那就是内存浪费问题,这个内存浪费不仅仅因为它常驻内存,更重要的是,对闭包的使用不当会造成无效内存的产生。

关于闭包,就我自己的习惯而言,能不用就不用,如果非用不可,那就想办法保持闭包对象的数量很少甚至唯一。

关于this:

this对象在运行时是基于函数的执行环境绑定的:在全局函数中,this等于window,当函数被当作某个对象的方法调用的时候,this等于那个对象,非对象的函数调用,this都会指向window。也就相当于说,谁调用这个函数,this就指向谁。

匿名函数的执行环境具有全局性。这时候this对象指向window。

每当创建一个函数的时候,函数在被调用的时候自动获取两个特殊的变量,this和arguments。

转载于:https://www.cnblogs.com/changyangzhe/p/5723127.html

你可能感兴趣的:(JavaScript的作用域与作用域链||闭包以及为什么要使用闭包||对this的理解)