简体   繁体   English

Javascript无限关卡

[英]Javascript Currying Infinite Levels

I recently saw some code that I just found out is called currying, I think. 我认为,最近我发现一些刚刚发现的代码称为currying。 The code looks like this: 代码如下:

layer.components[0]("ADBE Propety1")("ADBE Property 2")("ADBE Property 3");

The part I am interested in duplicating is the multiple sets of parenthesis after components[0] . 我感兴趣的复制部分是components[0]之后的多组括号。 Currying is new to me (as of today) and closures can get complicated. 咖喱对我来说是新的(到目前为止),关闭可能会变得很复杂。 So I need some help. 所以我需要一些帮助。

I would like to create a class where an instance of that class has children and I can get to the children by name like this: 我想创建一个该类的实例具有子类的类,并可以通过如下名称获取子类:

let bins = new Bins(proj);
console.log(bins('Videos')('More')('NiteLite_90.mp4')); 
// Return: {name: 'NiteLite_90.mp4', children: []}

With the code below, I can get two levels deep (down to 'More'), but not anywhere past that. 使用下面的代码,我可以深入到两个层次(降至“更多”),但除此之外没有任何其他层次。 I want to be able to go infinite levels deep. 我希望能够深入到无限的层次。

class Bins {
  constructor(proj) {
    this.bins = this._getBins(proj);
  }

  bin(name) {
    let bin = this.bins.filter(b => b.name === name)[0];
    if (bin) {
      return (name) => bin.children.filter(b => b.name === name)[0];
    } else {
      return null;
    }
  }

  _getBins(proj) {
    let { children } = proj;
    let childs = [];
    let self = this;
    children.forEach(child => {
      let obj = { name: child.name };
      if (child.children && child.children.length > 0) {
        obj.children = self._getChildren(child);
      }
      childs.push(obj);
    });
    return childs;
  }

  _getChildren(child) {
    let children = [];
    let self = this;
    child.children.forEach(c => {
      let obj = { name: c.name };
      if (c.children && c.children.length > 0) {
        obj.children = self._getChildren(c);
      }
      children.push(obj);
    });
    return children;
  }
}

let proj = {
  children: [
    {
      name: 'Videos',
      children: [
        {
          name: 'NiteLite_00.mp4',
          children: []
        },
        {
          name: 'More',
          children: [
            {
              name: 'NiteLite_90.mp4',
              chidlren: []
            }
          ]
        },
        {
          name: 'NiteLite_97.mp4',
          children: []
        }
      ]
    },
    {
      name: 'Sequences',
      children: [
        {
          name: 'Oregon Coast',
          children: []
        }
      ]
    },
    { name: 'Music', children: [] },
  ]
};
let bins = new Bins(proj);
console.log(bins.bin('Videos')('More')('NiteLite_90.mp4')); // I don't want to have to call `.bins` first

Could I get some help on setting this up? 我可以在设置方面获得一些帮助吗? I have researched multiple other currying posts on here and seen several blogs about it, but I still don't get it and I want some specific help with my code. 我在这里研究了多个其他易引起麻烦的帖子,并看到了一些有关它的博客,但是我仍然没有得到它,我想要一些有关代码的特定帮助。

You could have a recursive curry function to be able to go as deep as you want. 您可以具有递归的咖喱函数,以实现所需的深度。 But you have an additional problem: how do you know when to stop returning a function and to return the actual object? 但是您还有另一个问题:如何知道何时停止返回函数并返回实际对象?

If you call bins.bin('Video')('More') -- how do you know if you want to return the bin for the More object or a function that will search through children of More so you can find the 'NiteLite_90.mp4 bin? 如果调用bins.bin('Video')('More') -如何知道要返回More对象的bin还是要搜索More'NiteLite_90.mp4的函数,以便找到'NiteLite_90.mp4 bin?

Below is one possible solution that will give you both options: 以下是一种可能的解决方案,将为您提供两种选择:

class Bins {
  search(collection, name) {
    const bin = collection.find(b => b.name === name);
    if (bin) {
      // first create a function that will search through this bin's children
      const curry = (name) => this.search(bin.children, name);

      // but make the bin itself available through a `.bin` property on the function
      curry.bin = bin;

      // return this new function so it can be curried
      return curry;
    } else {
      return null;
    }
  }

  bin(name) {
    return this.search(this.bins, name);
  }

  // plus everything you already have in this class, except for the original
  // bin(name) function
}

Now you can go an unlimited number of levels deep, and have access to any intermediate bin as well via the .bin property: 现在,您可以进入无限多个级别,并可以通过.bin属性访问任何中间的bin:

let bins = new Bins(proj);

console.log(bins.bin('Videos').bin);
// { name: "Videos", children: [ ... ] }

console.log(bins.bin('Videos')('More').bin);
// { name: "More", children: [ ... ] }

console.log(bins.bin('Videos')('More')('NiteLite_90.mp4').bin);
// { name: "NiteLite_90.mp4" }

Like your original method, the search method can return null so take care when you're searching for a path that might not exist: 类似于您的原始方法, search方法可以返回null因此在搜索可能不存在的路径时要小心:

console.log(bins.bin('Videos')('DoesNotExist')('NiteLite_90.mp4').bin);
// Uncaught TypeError: bins.bin(...)(...) is not a function

console.log(bins.bin('Videos')('More')('DoesNotExist.mp4').bin);
// Uncaught TypeError: Cannot read property 'bin' of null

So you'll probably want to wrap such calls in a try/catch to be safe: 因此,您可能希望将此类调用包装在try/catch以确保安全:

let bin;
try {
  bin = bins.bin('Videos')('DoesNotExist')('NiteLite_90.mp4').bin;
} catch (e) {
  console.error('Bin not found!');
}

if (bin) {
  // do whatever you want with the found bin
}

Just create a function, which returns itself: 只需创建一个函数,该函数将返回自身:

class Foo{
  constructor(){
    function bar(){
      //Do something nice here
      return bar
    }
    this.bar=bar
  }
}

new Foo().bar(1)(2)(3)

Now, you can go infinitely deep... 现在,您可以无限深入……

new Foo().bar(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20) //Works perfectly 

Ok, but how to collect data of all calls? 好的,但是如何收集所有通话的数据呢?

This is where it start to get tricky. 这是开始变得棘手的地方。

  1. You can store somthing after each call on the 'owner' object of the function: 您可以在每次调用该函数的“所有者”对象后存储一些东西:
class Foo{
  constructor(){
    function bar(arg){
      baz.push(arg)
      return bar
    }
    const baz=[]
    this.bar=bar
    this.baz=baz
  }
}

const foo=new Foo()
foo.bar(1)(2)(3)

console.log(foo.baz) //[1,2,3]
  1. Or, pass some information between subsequent calls by bind ing this to the returned function, like this: 或者,通过传递后续调用之间的一些信息bind荷兰国际集团this返回的功能,就像这样:
class Foo{
  constructor(){
    function bar(...args){
      console.log(this)
      return bar.bind(
        this //Bound array
        .concat(args)
      )
    }
    this.bar=bar.bind([])
  }
}

new Foo().bar(1)(2)(3)()
//[]
//[1]
//[1,2]
//[1,2,3]

Is this something you are looking for? 这是您要找的东西吗?

class Animal {
  constructor (name) {
    console.log(`Im a bear called ${name}`)
  }

  shout (voice) {
    return (sound) => `I am making a ${sound} sound with a ${voice} voice` ;
  }
}

const harry = new Animal('Harry')
const speakUp = harry.shout('low')('woof')

console.log(speakUp)

Small explanation 小说明

The whole idea of currying is that functions return functions. currying的整个思想是函数返回函数。 So whereas you normally would do something like: 因此,尽管您通常会执行以下操作:

const hello = (name, age, job) => (
  `My nam is ${name}, I am ${age} and I do ${job} for a living`
)

with currying you'd do something like: 招惹你会做类似的事情:

const hello = (name, age, job) => (
  `My nam is ${name}, I am ${age} and I do ${job} for a living`
)

const helloWithCurrying = (name) => (age) => (job) => (
  `My nam is ${name}, I am ${age} and I do ${job} for a living`
)

console.log(hello('Rick', 26, 'coding'))
console.log(helloWithCurrying('Rick')(26)('coding'))

The benefit of currying really comes into play when you are having complex Higher Order Functions. 当您具有复杂的高阶函数时,curry的好处真正发挥了作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM