重学javascript之词法作用域和动态作用域

330 阅读3分钟

前言

上一篇:重学javascript之执行上下文栈juejin.cn/post/695048…

每个执行上下文都有三个重要的属性:

  • 变量对象
  • 作用域链
  • this 本章讲解:深入分析javascript之词法作用域和动态作用域

不忘初心,牢记使命

很多同学看到这里的时候可能会认为:“js基础太简单啦,有必要写吗?”。的确,相对于初学js的我们,现在回顾是很简单,但是自然界有一个规律的,也包括我们学习前端知识,那就是掌握真理,返璞归真,才能在走向升华的路上愈发坚实!

正文

说一下你对闭包的理解?

  • 概念的角度 从根本上理解闭包,其实闭包和我们javascript这门语言是有关系的,javascript和很多语言一样,都是采用的词法作用域而不是动态作用域,也就是函数作用域是依赖函数定义的时候产生的变量作用域(变量对象)。

而这个词法作用域是怎么实现的呢? javascript函数对象中不仅仅有函数逻辑的代码,还有当前作用域链的引用。然后函数对象通过这个作用域链相互关联起来,这样子函数内部的变量都可以保存在函数的作用域中。这个概念叫做闭包!

  • 技术的角度讲

其实所有的javascript函数都是闭包:他们都是对象,都有一个关联的作用域链,绝大多数函数在调用的时候的作用域链和定义的时候的作用域链都是相同的,但是也有不同的时候,这个时候闭包开始interesting,也就是我们常讲的把一个函数作为对象返回。

  • 通俗讲程序语言范畴内的闭包是指函数把其的变量作用域也包含在这个函数的作用域内,形成一个所谓的“闭包”这样的话外部的函数就无法访问内部变量,因此严格意义上所有的函数都是闭包。

  • 我们常常所讲的闭包指的是外部函数访问到内部的变量,这样可以在外部读取函数内部的变量,也可以把这些变量的值始终保存在内存中。

闭包的例子:

function counter (start) {
  var count = start;
  return {
    add: function () {
      count ++;
    },
    get: function () {
      return count;
    },
  };
}

var foo = counter (4);

foo.add();  
foo.get()   //5

上面代码,返回两个闭包函数,他们保留着对count的引用,因此这两个函数可以访问count,由于语言范畴内的闭包,因此除了使用闭包这种方式,我们不能在在外部强行对作用域进行引用或者赋值。

典型例子

for (var i = 0; i < 10; i++) {
  setTimeout (function () {
    console.log (i);    //10 10 10 ....
  }, 1000);
}

for (var i = 0; i < 10; i++) {
  (function (e) {
    setTimeout (function () {
      console.log (e);
    }, 1000);
  })(i);
}

for (var i = 0; i < 10; i++) {
  setTimeout((function(e) {
    return function() {
      console.log (e);
    }
  })(i), 1000);
}

什么是作用域?

  • 作用域是指程序源代码中定义变量的区域
  • 作用域规定了如何查找变量,也就是确定当前执行代码对变量的权限
  • javascript采用词法作用域即静态作用域。

静态作用域和动态作用域

javascritp采用的是词法作用域,也就是函数在定义的时候函数的作用域就已经确定了。

了解一下动态作用域: 动态作用域中函数的作用域是在函数执行的时候决定的。

举一个实例

var value = 1;
function foo() {
    console.log(value);
}
function bar() {
    var value = 2;
    foo();
}
bar();

结果:

词法作用域: 先从函数foo内部查找是否有局部变量value,如果没有就从上一层查找有没有该变量。》1

动态作用域: 如果没有的话就去调用的位置去寻找有没有该变量。》2