LoginSignup
2
2

More than 3 years have passed since last update.

jsでパイプライン演算子もどき7種

Last updated at Posted at 2020-07-21

javascriptでパイプライン演算子を使いたい

こういうやつ。

text = '{"0": "a", "1": "b"}';
text |> JSON.parse |> Object.entries |> (a=>new Map(a)) |> console.log;// これ
// console.log(new Map(Object.entries(JSON.parse(json)))); と同等

まだステージ1で使えないので、代替を考えてみる。

関数で実装

まずは素直に、初期値と関数を渡すと返り値を返すだけの関数を実装してみる。

define.js
const chain = (start, ...fns)=>{
  let result = start
  for (const fn of fns) {
    result = fn(result)
  }
  return result
}

使ってみる。

how-to-use.js
const result = chain(text,
  JSON.parse,
  Object.entries,
  a=>new Map(a)
)

console.log(result)

シンプルで見通しがいい。もうこれでいいんじゃないかな。

メソッドチェーン(もどき?)

せっかくなので、別の方法でも実装してみる。

define.js
const start = (val)=>{
  return {
    chain(fn) {return start(fn(val))},
    end() {return val}
  }
}
how-to-use.js
const result = start(text)
.chain(JSON.parse)
.chain(Object.entries)
.chain(a=>new Map(a))
.end()

console.log(result)

これはこれで、分かりやすくて良いと思う。

prototype拡張

直接メソッドチェーンしてみよう。

define.js
Object.defineProperty(Object.prototype, '_chain', {
  configurable: false,
  enumerable: false,
  value: function(fn){return fn(this)}
});
how-to-use.js
const result = text
._chain(JSON.parse)
._chain(Object.entries)
._chain(a=>new Map(a))

console.log(result)

Objectのprototypeを直接拡張するのは弊害が大きすぎるので、絶対やめましょう。prototype拡張を行うライブラリを読み込んでいる場合、名前がバッティングした時に死にます。

prototype拡張 その2

symbolを使う方法。

define.js
const chain = Symbol('chain');
Object.defineProperty(Object.prototype, chain, {
  configurable: false,
  enumerable: false,
  value: function(fn){return fn(this)}
});
how-to-use.js
const result = text
[chain](JSON.parse)
[chain](Object.entries)
[chain](a=>new Map(a))

console.log(result)

symbolを用いてprototype拡張すれば、ライブラリ等とバッティングせずに済むため安全安心。
但し、使いやすくは…ない。

Array.prototype.map()使用

一旦配列に入れると、配列操作系の関数を使うことができる。

how-to-use.js
const result = [text]
.map(JSON.parse)
.map(Object.entries)
.map(a=>new Map(a))
[0]

console.log(result)

配列から取り出すために、最後に[0]を付ける必要がある。いつか書き忘れそうで怖い。

Array.prototype.reduce()使用

reduceも使える。今度は関数の方が配列に入っているので注意。

how-to-use.js
const result = [
  JSON.parse,
  Object.entries,
  a=>new Map(a)
].reduce((val, fn)=>fn(val), text)

console.log(result)

そろそろパイプライン演算子とは程遠くなってきている。

Promise使用

Promiseのthen()の引数には関数を渡している。この仕様を、関数呼び出しのためだけに利用してみる。

how-to-use.js
//返り値を使用しない場合
new Promise(r=>r(text))
.then(JSON.parse)
.then(Object.entries)
.then(a=>new Map(a))

//返り値を受け取る場合
(async function(){
  const result = await new Promise(r=>r(text))
  .then(JSON.parse)
  .then(Object.entries)
  .then(a=>new Map(a))

  console.log(result)
})()

返り値を使いたいときに.then()で繋げるか、async関数内でawaitする必要がある。
非同期処理と関係ないところにPromiseをいきなり使ったら混乱しそうだなとは思う。

2
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2