JavaScript基础11.闭包与回调

闭包 + 回调 -> JS 高阶函数 High-order function

高阶函数要比普通函数处理更多的问题,更高的集成性和封装性
集成性:把所有的功能装到一个程序里面,进行相互的作用
封装性:将一个程序所需要的代码封装到函数内部

1.闭包 closure

理论上,test 就是一个闭包,
原因是 闭包在ECMA262 上的定义:当一个函数被系统创建时,闭包就会被一同创建出来
一个函数统一与外界环境(作用域)捆绑在一起的这种现象叫做闭包

闭包函数

// global scope -> 闭包
function test() { // 闭包函数
  
  // fn test1 + test scope -> 形成闭包
  // 函数test1 + test的局部作用域 -> 形成闭包
  // 这个test1函数就叫闭包函数
  function test1(){ // 闭包函数
  }
}

以下函数是否形成闭包

function b(a) {
  console.log(a);
}

function test(){
  var a = 1;
  b(a);
}

test();


不能,判断是否形成闭包只有一个要素,就是看function b是否能直接访问到 test 的环境,b 并不能与 test
的环境捆绑,所以,它不是闭包

在一个函数内部声明函数是否有意义?

function compute(){
  function plus(a, b){
    return a + b;
  }

   function minus(a, b){
    return a - b;
  }
  
   function mul(a, b){
    return a * b;
  }
  
   function div(a, b){
    return a / b;
  }
  
  return function(type, a, b){
    switch (type){
      case 'plus':
        return plus(a,b);
      case 'minus':
        return minus(a,b);
      case 'mul':
        return mul(a,b);
      case 'div':
        return div(a,b);
      deflaut:
        break;      
    }
  }
}
doCompute = compute();

const res = doCompute('mul', 1, 2);
console.log(res);
    function compute(type, a, b){
        compute.plus = function (a, b){
            return a + b;
        }

        compute.minus = function (a, b){
            return a - b;
        }

        compute.mul = function mul(a, b){
            return a * b;
        }

        compute.div = function div(a, b){
            return a / b;
        }

        return compute[type](a,b);
    }


    const res = compute('mul', 100, 2);
    console.log(res);

有意义,可以在函数内部集成子函数

闭包模拟变量私有化

class Compute{
  constructor(){
    this.a = 100;
  }
  add(b){
    return this.a + b;
  }
  minus(b){
    return this.a - b;
  }
}

var compute = new Compute();
// a -> Compute class private member
// a -> public member
console.log(compute.a);//100
console.log(compute.add(200));//300


如何将 a 变为私有成员,通过闭包模拟变量私有化

问:如何将 a 变为私有成员
答:通过闭包模拟变量私有化
// es6 实现
var Compute = (function (){
  var a = 100;
  class Compute{
    add(b){
      return a + b;
    }
    minus(b){
      return a - b;
    }
  }
  return Compute;
})();

var compute = new Compute();
console.log(compute.a);//报错
console.log(compute.add(200));//300

// es5实现
var Compute = (function (){
  var a = 100;
  function Compute(){};
  Compute.prototype.add = function(b){
    return a + b;
  }
  Compute.prototype.minus = function(b){
    return a - b;
  }
  return Compute; // 或者 window.Compute = Compute;
})();
var compute = new Compute();
console.log(compute.a);//报错
console.log(compute.add(200));//300

转移闭包,向外抛出闭包

将函数内部的闭包函数拉到外部使用,并且可以将外部作用域一起拉到外部来

function test(){
  var a = 1;
  
  // test scope
  
  function b(){ // 闭包函数
    return ++a;
  }
  // test scope(a) + fn b
  return b;
}

var bb = test();
bb(); // 2
bb(); // 3

JS function API

oApp = getElementById('app');

function getElementById(idStr){
  // idStr todo ---> node
  return node;
}

// -------------------------------------------
const app = express();
app.server();

function express() {
  return {
    server(){
      // todo...
    }
  }
}
function compute(){
  var baseNum = 1003;
  
  return {
    plus: function(a, b) { // JS function API
      return baseNum + a + b;
    },
    minus: function(a, b) {
      return baseNum -a - b;
    },
    mul: function(a, b) {
      return baseNum * a * b;
    },
    div: function(a, b) {
      return baseNum / a / b;
    }
  }
}

var comp = compute();
comp.plus(1, 2);

纯函数: 特点:

  1. 标准的输入输出,每次输入相同的值,返回的结果一定是相同的
  2. 不能够依赖外界,函数内部所有的东西都不能依赖外界
  3. 不可以影响外界,不能在函数内部去修改外界的环境

2. 回调 callback

call -> 调用 行为
trigger -> 触发,-> 事件的发生
call 针对的是 function
trigger 针对的是 event

event 和 function 的关系

  1. event 通过一个程序或者是交互被触发,并且执行相应的处理程序。一个元素box,user 通过 click 这个事件去点击了 box 这个元素,这个行为叫点击事件trigger
  2. 当一个 event 和一个 function被绑定了之后,这个事件一旦被触发,会引起 function 被调用

box.addEventListener(‘click’, handleClick, false);

  1. 在浏览器方面,事件 event 是被定义好的,默认存在的。box 存在 click, mouse 相关的事件
  2. -> 选择一个事件click -> 绑定一个操作程序handler ->当 click 被 trigger 的时候,就会执行 handler(function)

绑定的是一个事件处理函数,而不是一个事件,绑定的是一个函数,而不是函数执行。当针对于 box 的 click 被 trigger
的时候,去执行这个 handler 函数,这叫做 function call, event trigger

  1. callback

情景
function a(){
// task 1
// task1 要完成,必须要通过 a 这个程序
// 另外的函数 b来完成
}

function a (){
  // todo task 1;
  
  function b(){
    // continue task
    // todo...
    // task 1 is finished
  }
  
  b();
}

a();

// task1 finished
//需求:分别完成
// 通过回调完成
function a(callback){
  // todo task 1;
  // var res = task 1  1/2
  callback && callback(res);
}

a(function(res){
  // get res
  // go on doing task 1
})

// 这个回调的作用:相当于 a 函数是一个封装,通过回调拿到封装的结果,然后继续完成我的业务
// task1 finished

两个任务:1. 返回值2. 执行回调

function test(count, callback){
  const newCount = count + 1;
  const callbackCount = count + 100;
  
  callback && callback(callbackCount);
  return newCount;
}

var newCount = test(123, function(callbackCount) {
  console.log(callbackCount);
});
console.log(newCount);

通过闭包和回调完成计算,同时打印日志

function Compute(callback){
  return function(a, b, type){
    let result = 0;
    switch(type){
      case '+':
        result = a + b;
        break;
      case '-':
        result = a - b;
        break;
      case '*':
        result = a * b;
        break;
      case '/':
        result = a / b;
        break;
      default:
        break;
    }
    callback && callback(a, b, type, result);
  }
}
var compute = Compute(function(a, b, type, result){
  console.log(`${a} ${type} ${b} = ${result}`);
});
compute(1, 2, '-');
compute(100, 200, '+');

回调做验证

function compute(validator){
  return function(a, b, type) {
    var { isError, errorMsg } = validator(a, b);
    if(isError){
      throw new Error(errorMsg);
    }
    
    switch(type){
      case '+':
        return a + b;
      case '-':
        return a - b;
      case '*':
        return a * b;
      case '/':
        return a / b;
      default:
        break;
    }
  };
}

var comp = compute(validator);
console.log(comp(1, 2, '+'));

function validator(a, b){
  if(a > 50 || b > 30){
    return {
      isError: true,
      errorMsg: 'a 必须小于等于50,b必须小于等于30'
    }
  }
  return {
      isError: false,
      errorMsg: 'ok'
    }
}

例4

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
<script>
  var oLi = document.getElementsByTagName('li');
  var i = 0; // 3
  for(; i < oLi.length; i++){
    oLi.onclick = function() { 
      // 绑定的是事件处理函数,而不是函数执行
      console.log(i); 
      // 捆绑了外界的作用域
    }
  }

</script>

解决方法

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
<script>
  var oLi = document.getElementsByTagName('li');
  var i = 0; // 3
  
 // 解决方法
  for(; i < oLi.length; i++){
    //创建了函数->执行了->i->function
    (function(i){ 
      //var li = i;
      // 函数的参数 -> 充当函数的临时局部变量
      oLi[i].onclick = function() { 
        // 捆绑了立即执行函数的作用域
        console.log(i); // 0, 1, 2 拿的是立即执行函数中的参数
      }
    })(i)
  }
</script>

闭包缺点:内存溢出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值