愿你坚持不懈,努力进步,进阶成自己理想的人

—— 2017.09, 写给3年后的自己

Javascript学习总结——闭包与生成器

闭包

1、函数可以作为结果返回,比如我们想实现一个延迟执行的求和函数,可以这么写:

function lazySum(arr) {
  var sum = function() {
    return arr.reduce((x, y) => {
      return x+y;
    });
  }
  return sum;
}

var fn = lazySum([1, 3, 5]);
fn(); // 返回9

也就是说,闭包可以保存函数内部的局部变量(闭包是可以保存状态的函数)
2、对于以下的例子:

function count() {
  var arr = [];
  for(var i=1; i<=3; i++) {
    arr.push(() => {
      console.log("runs!");
      return i*i;
    });
  }
  return arr;
}

var results = count();
var f1 = results[0];
console.log( f1() ); // 返回的是1吗?错,而是16

会出现这样子的情况,是因为闭包函数并不会立即执行,故闭包中保存的i,是循环执行完之后的值。故 闭包中不宜引用循环变量,或者后续会发生变化的变量,那如果一定要在闭包中使用循环变量,如何处理呢?如下:

function count() {
  var arr = [];
  for(var i=1; i<=3; i++) {
    arr.push((function(n){
      return function() {
        return n*n;
      }
    })(i));
  }
  return arr;
}

var results = count();
var f1 = results[0];
console.log( f1() ); // 返回1

其中,立即执行函数的写法为:(函数声明体)(实参列表),如:

(function(x, y){
  console.log(x+y);
})(1, 2);
// 输出3

3、使用闭包,封装一个私有变量(实现private变量的封装)

function CreateStudent() {
  var name;
  return {
    setName: _name => {
      name = _name;
    },
    name: () => {
      return name;
    },
    display: () => {
      console.log("The student is " + name);
    }
  }
}

var stu = CreateStudent();
stu.setName("RuphiLau");
stu.name = "Ruphi";
stu.display(); // 输出The student is RuphiLau



生成器(generator)

生成器是可以返回多次的函数,故也可以保存函数的状态

1、生成器的声明:声明一个生成器,在function关键字后面加一个*号,同时每次均用yield关键词返回

function* fn() {
  var count = 0;
  while(true) {
    yield count++;
  }
}

2、生成器的使用,如下:

var f = fn();
f.next(); // 返回{value: 0, done: false}
f.next(); // 返回{value: 1, done: false}
f.next(); // 返回2

调用返回一个包含value和done键值的对象,value存放每次返回的值,done是是否到达生成序列末尾的标志(有限的generator,最后是可以用return返回)。另外,我们还可以使用for ... of循环迭代generator对象,不用手动判断是否done了