Análise detalhada e desenvolvimento de functores javascript

A análise detalhada e o desenvolvimento de functores

Recentemente, estudei no campo de treinamento altamente remunerado do front end da Dragou University. Aprendi muitos conhecimentos que não conhecia antes. Experimentei muitos conceitos e métodos de programação de aprendizagem de programação. Obrigado ao Sr. Cui Cui, a turma e o instrutor

A seguir está um resumo do meu estudo de functores

Deseja processar a string, por exemplo, todos se tornam caracteres maiúsculos e retornam uma nova string

 let str = "la gou jiao yu"
 let UpperStr = str.toUpperCase()
 console.log(UpperStr)

É muito simples. Pode ser facilmente implementado pelo método toUpperCase , mas esse método é para manipular diretamente str, para que possamos envolvê-lo em um contêiner e operar esse contêiner para realizar indiretamente a operação dos dados, vamos operar o functor, deixe o functor operar a própria string, de modo a conseguir manipulação indireta da string, para que possamos nos concentrar no método, independentemente do que seja

Portanto, este contêiner é na verdade um functor

Functores básicos, que é o contêiner mais primitivo

class Container {
    
    
  constructor (value) {
    
    
    this._value = value
  }
  map (fn) {
    
    
    return new Container(fn(this._value))
  }
}
let r = new Container(str).map( x => x.toUpperCase())
console.log(r)

Desta forma, podemos usar o functor para introduzir a string de operação, o que melhora muito a segurança

Em seguida, vamos melhorar este functor. É inconveniente fazer um novo sempre que o chamamos , então vamos usar um método de para otimizar este functor.

Otimização básica do functor

class Container {
    
    
  static of (value) {
    
    
    return new Container(value)
  }
 // 这样一来在函子内部就可以完成 new 的操作 ,不用每一次都new一个新的函子对象出来
  constructor (value) {
    
    
    this._value = value
  }

  map (fn) {
    
    
    return Container.of(fn(this._value))
  }
}
let str = "la gou jiao yu"


let r = Container.of(str).map( x => x.toUpperCase())
console.log(r)

Nesse processo, você também pode fazer chamadas encadeadas para obter o processamento contínuo de strings

let r = Container.of(str)
.map( x => x.toUpperCase())
.map(x => x.slice(0,6))
.map(x => x.toLowerCase())
console.log(r)

Mas, em muitos casos, a chamada de função não é fácil, é muito provável que o valor passado esteja vazio

MayBe functors

Então temos que determinar se o valor passado está vazio, para que seja possível ter o functor MayBe, o que significa que este functor está lidando com possíveis vagas

// MayBe 函子
class MayBe {
    
    
  static of (value) {
    
    
    return new MayBe(value)
  }

  constructor (value) {
    
    
    this._value = value
  }

  map (fn) {
    
    
    return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
  }

  isNothing () {
    
    
    return this._value === null || this._value === undefined
  }
}

Começamos a otimizar com base em um functor e mudamos o nome para Maybe, que é o functor Maybe.

let r = MayBe.of(null)
.map( x => x.toUpperCase())
.map(x =>x.slice(0,6))
.map(x => x.toLowerCase())
console.log(r)
// 现在我们的函子拥有了处理空值的能力

Resultado:
Insira a descrição da imagem aqui

qualquer um dos functor

Mas, no processo de chamada, queremos saber qual etapa da chamada tem um problema, então temos o functor Either

O significado de qualquer um em inglês é um dos dois, ou ... ou ..., ou ... ou ...

Portanto, qualquer um dos functor consiste em duas partes


class Left {
    
    
  static of (value) {
    
    
    return new Left(value)
  }

  constructor (value) {
    
    
    this._value = value
  }

  map (fn) {
    
    
    return this
  }
}

class Right {
    
    
  static of (value) {
    
    
    return new Right(value)
  }

  constructor (value) {
    
    
    this._value = value
  }

  map (fn) {
    
    
    return Right.of(fn(this._value))
  }
}

Mais tarde, para uma melhor compreensão, fiz qualquer um deles parecer mais com um functor

let Either = {
    
    Left ,Right} 
// 我把这两个方法存到了一个Either对象里
function parseJSON (str) {
    
    
  try {
    
    
    return Either.Left.of(JSON.parse(str))
  } catch (e) {
    
    
    return Either.Right.of({
    
     error: e.message })
  }
}
let r = parseJSON('{ "name": "zs" }')
          .map(x => x.name.toUpperCase())
console.log(r)

Desta forma, chamando métodos diferentes no objeto Either, o efeito de esquerda ou direita é alcançado

Functores IO

O functor se desenvolveu até este ponto. Uma vez que os dados podem ser processados, os métodos e funções devem ser capazes de processá-los?

Portanto, existem functores IO, usados ​​para lidar indiretamente com as ações da função

// IO 函子
const fp = require('lodash/fp')
// 引入函数式编程的库lodash
class IO {
    
    
  static of (value) {
    
    
    return new IO(function () {
    
    
      return value
    })
  }

  constructor (fn) {
    
    
    this._value = fn
  }

  map (fn) {
    
    
    return new IO(fp.flowRight(fn, this._value))
      //函数组合
  }
}

// 调用
let r = IO.of(process).map(p => p.execPath)
// console.log(r)
console.log(r._value())

Monad functores

Por se tratar de uma chamada de função, existe alguma relação de aninhamento, por exemplo, o código a seguir, existe o fenômeno de IO (IO (x)), o functor é chamado no functor, que não é conciso

// IO 函子的问题
const fs = require('fs')
const fp = require('lodash/fp')

class IO {
    
    
  static of (value) {
    
    
    return new IO(function () {
    
    
      return value
    })
  }

  constructor (fn) {
    
    
    this._value = fn
  }

  map (fn) {
    
    
    return new IO(fp.flowRight(fn, this._value))
  }
}

let readFile = function (filename) {
    
    
  return new IO(function () {
    
    
    return fs.readFileSync(filename, 'utf-8')
  })
}

let print = function (x) {
    
    
  return new IO(function () {
    
    
    console.log(x)
    return x
  })
}

let cat = fp.flowRight(print, readFile)
// IO(IO(x))
let r = cat('package.json')._value()._value()
console.log(r)

Para resolver este problema, o functor Monad foi introduzido. Monad significa mônada em inglês, e também significa uma entidade indivisível. Assim, os functores Monad são criados para resolver os functores IO acima e atingir o nivelamento.

// IO Monad
const fs = require('fs')
const fp = require('lodash/fp')

class IO {
    
    
  static of (value) {
    
    
    return new IO(function () {
    
    
      return value
    })
  }

  constructor (fn) {
    
    
    this._value = fn
  }

  map (fn) {
    
    
    return new IO(fp.flowRight(fn, this._value))
  }

  join () {
    
    
    return this._value()
  }

  flatMap (fn) {
    
    
    return this.map(fn).join()
  }
}

let readFile = function (filename) {
    
    
  return new IO(function () {
    
    
    return fs.readFileSync(filename, 'utf-8')
  })
}

let print = function (x) {
    
    
  return new IO(function () {
    
    
    console.log(x)
    return x
  })
}

let r = readFile('package.json')
          // .map(x => x.toUpperCase())
          .map(fp.toUpper)
          .flatMap(print)
          .join()

console.log(r)

Após resolver o problema de nivelamento, para funções, um dos problemas mais importantes é o problema assíncrono, e então o functor Tarefa é criado para lidar com o problema assíncrono.

// Task 处理异步任务
const fs = require('fs')
const {
    
     task } = require('folktale/concurrency/task')
const {
    
     split, find } = require('lodash/fp')

function readFile (filename) {
    
    
  return task(resolver => {
    
    
    fs.readFile(filename, 'utf-8', (err, data) => {
    
    
      if (err) resolver.reject(err)

      resolver.resolve(data)
    })
  })
}

readFile('package.json')
  .map(split('\n'))
  .map(find(x => x.includes('version')))
  .run()
  .listen({
    
    
    onRejected: err => {
    
    
      console.log(err)
    },
    onResolved: value => {
    
    
      console.log(value)
    }
  })

Até agora, o functor foi apresentado. Na verdade, a ideia do functor é muito simples, é manipular dados e funções indiretamente, então eles são empacotados em um contêiner, e este contêiner é o functor

Para atender a diferentes necessidades, uma variedade de functores são derivados, então às vezes o foco do aprendizado de programação é descobrir quais problemas estão na base e como resolvê-los. Na verdade, todas as novas tecnologias são constantemente otimizadas. Melhore e resolva alguns problemas subjacentes.

ap(find(x => x.includes('version')))
  .run()
  .listen({
    
    
    onRejected: err => {
    
    
      console.log(err)
    },
    onResolved: value => {
    
    
      console.log(value)
    }
  })

Até agora, o functor foi apresentado. Na verdade, a ideia do functor é muito simples, é manipular dados e funções indiretamente, então eles são empacotados em um contêiner, e este contêiner é o functor

Para atender a diferentes necessidades, uma variedade de functores são derivados, então às vezes o foco do aprendizado de programação é descobrir quais problemas estão na base e como resolvê-los. Na verdade, todas as novas tecnologias são constantemente otimizadas. Melhore e resolva alguns problemas subjacentes.

Em segundo lugar, a ideia de operação indireta também vale a pena aprender. Este método não invadirá o objeto original e é muito seguro. No princípio da vinculação de dados bidirecional do vue, Object.defineProperty é usado para monitorar e operar o objeto, mas após a introdução do proxy, é muito seguro monitorar o objeto original monitorando o objeto instanciado pelo proxy.É uma idéia de programação muito importante não manipular diretamente os dados originais.

Acho que você gosta

Origin blog.csdn.net/qq_43377853/article/details/112312377
Recomendado
Clasificación