高阶函数

2020-08-06  本文已影响0人  育儿与心理

NodeJS 系列文章,本篇是第一篇,首先,预计将后续高频使用逻辑串一遍,依次是高阶函数,promise以及事件机制。本篇主要是高阶函数。

call、bind、apply

this 指向

this 指向问题,谁调用,我就指向谁。

全局上下文

非严格模式和严格模式中this都是指向顶层对象(浏览器中是window)。

  console.log(this === window); // true
  'use strict'
  console.log(this === window); // true
  this.name = 'Hiraku';
  console.log(this.name); // Hiraku

函数上下文

普通函数调用模式

  var name = '骁妈';
  var func = function() {
    console.log(this === window, this.name); // true 骁妈(浏览器)
  }; 

  func();

在 ES5 中,全局变量是挂载在顶层对象(浏览器是 window )中。

  const name1 = 'mahongqin';
  const func1 = function() {
    console.log(this === window, this.name1); // true undefined
  };
  const func2 = () => {
    console.log(this === window, this.name1); // true undefined
  };
  func1();
  func2();

let 没有给顶层对象中(浏览器是 window )添加属性,window.name1 和 window.func1,window.func2 都是 undefined。

严格模式中,普通函数中的 this 则表现不同,表现为 undefined。

  'use strict';
  var name2 = '骁妈';
  var func3 = function() {
    console.log(this === window); // false
  }; 

  func3();

call,apply 作用之一就是改变函数的 this 指向为第一个参数。 如果第一个参数是 undefined 或者 null,非严格模式下,是指向 window。严格模式下,就是指向第一个参数。

对象中的函数(方法)调用模式

  var name3 = 'Hiraku_Ma';
  var func4 = function() {
    console.log(this, this.name);
  }
  var obj = {
    name: 'mhq',
    func: func4,
    attr: {
      name: 'mhq-gs',
      func: func4,
    }
  }
  obj.func(); // Object{} obj 'mhq'
  obj.attr.func(); // Object{} obj.attr 'mhq-gs'
  // 用call类比则为:
  obj.func.call(obj); // 'mhq'
  // 用call类比则为:
  obj.attr.func.call(obj.attr); // 'mhq-gs'

函数赋值之后变成普通函数。

构造函数调用模式

function Example(type){
  this.type = type;
  console.log(this);
  // return this;
}
var exp = new Example('mhq');

使用 new 操作符调用函数,会自动执行以下步骤:

new 操作符调用时,this 指向生成的新对象。 new 调用时的返回值,如果没有显式返回对象或者函数,才是返回生成的新对象。

  function Example(type){
    this.type = type;
    console.log(this);
    return {};
  }
  var exp = new Example('mhq'); // { name: mhq }
  console.log(exp);
  // 如果返回函数 f ,则 exp 是函数 f,如果是对象 {} ,则 exp 是对象 {}

原型链中的调用模式

  function Example(type){
    this.type = type;
    console.log(this);
  }
  Example.prototype.func = function() {
    console.log(this.type);
  };
  var exp = new Example('mhq');
  exp.func();

箭头函数调用模式

  var name = 'mhq';
  var student = {
    name: 'hiraku',
    func: function () {
      var arrowFunc = () => {
        console.log(this.name);
      }
      arrowFunc();
    },
    arrowFunc2: () => {
      console.log(this.name);
    }
  }
  student.func(); // 'hiraku'
  student.arrowFunc2(); // 'mhq'
  var obj = {
    name: 'mhq',
    func: function () {
      console.log(this.name);
      return () => {
        console.log('arrowFn:', this.name);
      }
    }
  }
  var obj1 = {
    name: 'hiraku',
  }
  obj.func().call(obj1); // 'mhq'  'arrowFn:' 'mhq'
  obj.func.call(obj1)(); // 'hiraku' 'arrowFn:' 'hiraku'

DOM 事件处理函数调用

高阶函数(Higher-order function)

什么是高阶函数?高阶函数至少满足两个条件:接受一个或多个函数作为输入,输出一个函数。也可以这样理解,一个函数的参数是一个函数(回调函数);一个函数返回一个函数(函数柯里化)。

我们写代码时不希望破坏原有逻辑,所以将原有逻辑包上一个函数,这个函数内部实现自己的逻辑,即切片编程。

  const originFunc = (...args) => {
    console.log('原函数', args);
  };

  // 希望在调用 originFunc 之前做一些事情,使用 Function.prototype 给每个函数扩展一些功能
  Function.prototype.before = function(cb) {
    return (...args) => {
      cb();
      this(...args);
    };
  };

  let newFunc = originFunc.before(() => {
    console.log('before originFunc');
  });

  newFunc('a', 'b', 'c');

一个异步并发问题

我同时发送多个请求,希望拿到最终的结果

  function after(time, fn) {
    return () => {
      if (--time === 0) {
        fn();
      }
    };
  }

  const exaFun = after(3, () => {
    console.log('执行');
  });

  exaFun();
  exaFun();
  exaFun();

示例:

  const fs = require('fs');
  const path = require('path');

  function run(times, fn) {
    const renderObj = {};
    return (key, value) => {
      renderObj[key] = value;
      console.log(key, value);
      if(--times === 0) fn(renderObj);
    };
  }

  let out = run(2, (result) => {
    console.log(result);
  });

  fs.readFile(path.resolve(__dirname, '1.txt'), 'utf8', (_, data) => {
    console.log(data, 'ddd')
    out('1', data);
  });
  fs.readFile(path.resolve(__dirname, '2.txt'), 'utf8', (_, data) => {
    out('2', data);
  });

函数柯里化

柯里化,即 Currying,为了给多参数函数提供了一个递归降解的方式,把提供多个参数的函数变换成接受一个单一参数的函数,并且返回余下参数而且返回结果的新函数。

  const curring = (fn, arr = []) => {
    // 长度值的是参数个数
    let len = fn.length;
    return (...arg) => {
      arr = [...arr, ...arg];
      if (arr.length < len) return curring(fn, arr);
      return fn(...arr);
    };
  };

  const add = (a, b, c, d, e, f, g, h) => {
    return a + b + c + d + e + f + g + h;
  }

  console.log(curring1(add, [1, 2])(3, 4)(5)(6)(7, 8)) // 15

函数柯里化使用场景:

发布订阅、观察者模式

观察者模式

  const fs = require('fs');
  const e = {
    arr: [],
    on(fn) {
      this.arr.push(fn);
    },
    emit() {
      this.arr.forEach(fn => fn());
    }
  };

  e.on(() => {
    console.log('读取到了数据');
  })
  const renderObj = {};
  e.on(() => {
    if (Object.keys(renderObj).length === 2) {
      console.log('都读取完毕了');
    }
  });

  fs.readFile('./name.txt', 'utf8', (_, data) => {
    renderObj['name'] = data;
    e.emit();
  });
  fs.readFile('./age.txt', 'utf8', (_, data) => {
    renderObj['age'] = data;
    e.emit();
  });

发布订阅

  class Subject { // 被观察者
    constructor(name) {
      this.name = name;
      this.arr = [];
      this.state = '我很开心';
    }
    attach(observer) { // 注册观察者 基于发布订阅
      this.arr.push(observer);
    }
    setState(newState) {
      this.state = newState;
      this.arr.forEach(o => o.update(this)); // 通知所有观察者 我的状态发生了变化
    }
  }

  class Observer { // 观察者
    constructor(name) {
      this.name = name;
    }
    update(s) {
      console.log(s.name + '当前状态是' + s.state + '对:' + this.name);
    }
  }

  let s = new Subject('小宝宝');
  let o1 = new Observer('我');
  let o2 = new Observer('我媳妇');

  s.attach(o1);
  s.attach(o2);
  console.log(s.state);
  s.setState('不开心了');

本篇文章涉及到的高阶函数使用有函数柯里化、发布订阅|观察者模式等。下篇将分享 Promise 源码,手写 Promise。

上一篇下一篇

猜你喜欢

热点阅读