javascript代码实例教程-JavaScript系列----作用域链和闭包

发布时间:2019-01-19 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了javascript代码实例教程-JavaScript系列----作用域链和闭包脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
小宝典致力于为广大程序猿(媛)提供高品质的代码服务,请大家多多光顾小站,小宝典在此谢过。 1.作用域链

 1.1.什么是作用域

谈起作用域链,我们就不得不从作用域开始谈起。因为所谓的作用域链就是由多个作用域组成的。那么, 什么是作用域呢?

 

 1.1.1作用域是一个函数在执行时期的执行环境。

每一个函数在执行的时候都有着其特有的执行环境,ECMAScript标准规定,在javascript中只有函数才拥有作用域。换句话,也就是说,JS中不存在块级作用域。比如下面这样:

 

复制代码

function getA() {

  if (false) {

    VAR a = 1;

  }

  console.LOG(a);  //undefined

}

getA();

function getB() {

  console.log(b);

}

getB();    // ReferenceError: b is not defined

复制代码

  上面的两段代码,区别在于 :getA()函数中,有变量a的声明,而getB()函数中没有变量b的声明。

 

  另外还有一点,关于作用域中的声明提前。

 

1.1.2.作用域中声明提前

在上面的getA()函数中,或许你还存在着疑惑,为什么a="undefined"呢,具体原因就是因为作用域中的声明提前:所以getA()函数和下面的写法是等价的:

 

 

function getA(){

   var a;

  if(false){

    a=1

    };

  console.log(a);

 

既然提到变量的声明提前,那么只需要搞清楚三个问题即可:

 

  1.什么是变量

 

      2.什么是变量声明

 

      3.声明提前到什么时候。

 

什么是变量?

 

  变量包括两种,普通变量和函数变量。 

 

普通变量:凡是用var标识的都是普通变量。比如下面 :

 var x=1;               

var object={};

var  getA=function(){};  //以上三种均是普通变量,但是这三个等式都具有赋值操作。所以,要分清楚声明和赋值。声明是指 var x; 赋值是指 x=1; 

 

 

函数变量:函数变量特指的是下面的这种,fun就是一个函数变量。

function fun(){} ;// 这是指函数变量. 函数变量一般也说成函数声明

 

类似下面这样,不是函数声明,而是函数表达式

 

var getA=function(){}      //这是函数表达式

var getA=function fun(){}; //这也是函数表达式,不存在函数声明。关于函数声明和函数表达式的区别,详情见javascript系列---函数篇第二部分

 

 

什么是变量声明?

 

     变量有普通变量和函数变量,所以变量的声明就有普通变量声明和函数变量声明。

 

 普通变量声明

var x=1; //声明+赋值

var object={};   //声明+赋值

 上面的两个变量执行的时候总是这样的

 

var x = undefined;      //声明

var object = undefined; //声明

x = 1;                  //赋值

object = {};            //赋值

关于声明和赋值,请注意,声明是在函数第一行代码执行之前就已经完成,而赋值是在函数执行时期才开始赋值。所以,声明总是存在于赋值之前。而且,普通变量的声明时期总是等于undefined.

 

函数变量声明

函数变量声明指的是下面这样的:

function getA(){}; //函数声明

声明提前到什么时候?

 

        所有变量的声明,在函数内部第一行代码开始执行的时候就已经完成。-----声明的顺序见1.2作用域的组成

 

1.2.作用域的组成

函数的作用域,也就是函数的执行环境,所以函数作用域内肯定保存着函数内部声明的所有的变量。

 

一个函数在执行时所用到的变量无外乎来于下面三种:

 

1.函数的参数----来源于函数内部的作用域

 

2.在函数内部声明的变量(普通变量和函数变量)----也来源于函数内部作用域

 

3.来源于函数的外部作用域的变量,放在1.3中讲。

 

比如下面这样:

 

var x = 1;

function add(num) () {

  var y = 1; 

  return x + num + y;   //x来源于外部作用域,num来源于参数(参数也属于内部作用域),y来源于内部作用域。

}

        那么一个函数的作用域到底是什么呢?

 

   在一个函数被调用的时候,函数的作用域才会存在。此时,在函数还没有开始执行的时候,开始创建函数的作用域:

 

  函数作用域的创建步骤:

 

          1. 函数形参的声明。

 

          2.函数变量的声明

 

          3.普通变量的声明。  

 

          4.函数内部的this指针赋值

 

             ......函数内部代码开始执行!  

 

         所以,在这里也解释了,为什么说函数被调用时,声明提前,在创建函数作用域的时候就会先声明各种变量。

 

   关于变量的声明,这里有几点需要强调

 

   1.函数形参在声明的时候已经指定其形参的值。  

 

function add(num) {

  var num;

  console.log(num);   //1

}

add(1);

 

 

  2.在第二步函数变量的生命中,函数变量会覆盖以前声明过的同名声明。

 

 

function add(num1, fun2) {

  function fun2() {

    var x = 2;

  }

  console.log(tyPEof num1); //function  

  console.log(fun2.toString()) //functon fun2(){ var x=2;}

}

add(function () {

}, function () {

  var x = 1

}); 

 

 

 

3.  在第三步中,普通变量的声明,不会覆盖以前的同名参数

 

function add(fun,num) {

  var fun,num;

  console.log(typeof fun) //function

  console.log(num);      //1

}

add(function(){},1);

 

 

   在所有的声明结束后,函数才开始执行代码!!! 

 

 

 

 1.3.作用域链的组成

 

 

   在JS中,函数的可以允许嵌套的。即,在一个函数的内部声明另一个函数

 

    类似这样: 

 

function A(){

  var  a=1;

   function B(){  //在A函数内部,声明了函数B,这就是所谓的函数嵌套。

         var b=2;   

   }

}

 

 

   对于A来说,A函数在执行的时候,会创建其A函数的作用域, 那么函数B在创建的时候,会引用A的作用域,类似下面这样

 

  

 

函数B在执行的时候,其作用域类似于下面这样:

 

 

 

    从上面的两幅图中可以看出,函数B在执行的时候,是会引用函数A的作用域的。所以,像这种函数作用域的嵌套就组成了所谓的函数作用域链。当在自身作用域内找不到该变量的时候,会沿着作用域链逐步向上查找,若在全局作用域内部仍找不到该变量,则会抛出异常。

 

 

 

 2.闭包

首先,要认识到一点,闭包是为了解决一种问题。那么,它是为了解决什么问题呢?

 

看下面的这道例题:

 

 

var funB,

funC;

(function() {

  var a = 1;

  funB = function () {

    a = a + 1;

    console.log(a);

  }

  funC = function () {

    a = a + 1;

    console.log(a);

  }

}());

funB();  //2

funC();  //3.

 

 

 

对于 funB和funC两个函数,在运行的时候,就会改变函数A中的变量a的值,这种情况就会污染了a变量。

 

 

 

 

 

 

这这幅图中,变量a可以被函数funB和funC改变,就相当于外部作用域链上的变量对内部作用域来说都是静态的变量,这样,就很容易造成变量的污染。还有一道最经典的关于闭包的例题:

 

var array = [

];

for (var i = 0; i < 10; i++) {

  var fun = function () {

    console.log(i);

  }

  array.push(fun);

}

var index = array.length;

while (index > 0) {

  array[--index]();

} //输出结果 全是10;

 

 

 

想这种类似问题产生的根源就在于,没有注意到外部作用域链上的所有变量均是静态的。

 

所以,为了解决这种变量的污染问题进而引入了闭包!

 

 那种它是如何解决的呢?  思想就是: 既然外部作用域链上的变量时静态的,那么将外部作用域链上的变量拷贝到内部作用域不就可以啦!! 具体怎么拷贝,当然是通过函数传参的形式啊。

 

以第一道例题为例

 

 

var funB,funC;

(function () {

  var a = 1;

  (function () {

    funB = function () {

      a = a + 1;

      console.log(a);

    }

  }(a));

  (function (a) {

    funC = function () {

      a = a + 1;

      console.log(a);

    }

  }(a));

}());

funB()||funC();  //输出结果全是2 另外也没有改变作用域链上a的值。

觉得可用,就经常来吧! 脚本宝典 欢迎评论哦! js脚本,巧夺天工,精雕玉琢。小宝典献丑了!

脚本宝典总结

以上是脚本宝典为你收集整理的javascript代码实例教程-JavaScript系列----作用域链和闭包全部内容,希望文章能够帮你解决javascript代码实例教程-JavaScript系列----作用域链和闭包所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。