Javascript 柯里化无限关卡
Javascript Currying Infinite Levels
我最近看到了一些我刚发现的代码,我认为它被称为柯里化。
代码如下所示:
layer.components[0]("ADBE Propety1")("ADBE Property 2")("ADBE Property 3");
我有兴趣复制的部分是components[0]
之后的多组括号。 Currying 对我来说是新的(截至今天),闭包会变得复杂。所以我需要一些帮助。
我想创建一个 class,其中 class 的一个实例具有 children,我可以通过这样的名称访问 children:
let bins = new Bins(proj);
console.log(bins('Videos')('More')('NiteLite_90.mp4'));
// Return: {name: 'NiteLite_90.mp4', children: []}
使用下面的代码,我可以获得两个级别的深度(低至 'More'),但不能超过那个级别。我希望能够无限深入。
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
我能得到一些设置方面的帮助吗?
我已经研究了这里的多个其他 currying 帖子并看到了几个关于它的博客,但我仍然不明白,我需要一些关于我的代码的具体帮助。
这是您要找的东西吗?
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)
小解释
柯里化的整个思想是函数 return 函数。所以你通常会做这样的事情:
const hello = (name, age, job) => (
`My nam is ${name}, I am ${age} and I do ${job} for a living`
)
用柯里化你会做这样的事情:
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'))
当您拥有复杂的高阶函数时,柯里化的好处真正发挥作用。
只需创建一个函数,returns 本身:
class Foo{
constructor(){
function bar(){
//Do something nice here
return bar
}
this.bar=bar
}
}
new Foo().bar(1)(2)(3)
现在,你可以无限深入...
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
好的,但是如何收集所有通话的数据?
这是开始变得棘手的地方。
- 您可以在每次调用函数的 'owner' 对象后存储一些东西:
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]
- 或者,通过
bind
ing 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]
你可以有一个递归的 curry 函数来达到你想要的深度。但是你还有一个额外的问题:你怎么知道什么时候停止 return 函数并 return 实际对象?
如果你调用 bins.bin('Video')('More')
-- 你怎么知道你是想 return More
对象的 bin 还是搜索 [=15 的子对象的函数=] 所以你可以找到 'NiteLite_90.mp4
bin?
以下是一种可能的解决方案,可为您提供两种选择:
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
}
现在您可以深入无限数量的级别,并且可以通过 .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" }
与您原来的方法一样,search
方法可以 return 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
所以为了安全起见,您可能希望将此类调用包装在 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
}
我最近看到了一些我刚发现的代码,我认为它被称为柯里化。 代码如下所示:
layer.components[0]("ADBE Propety1")("ADBE Property 2")("ADBE Property 3");
我有兴趣复制的部分是components[0]
之后的多组括号。 Currying 对我来说是新的(截至今天),闭包会变得复杂。所以我需要一些帮助。
我想创建一个 class,其中 class 的一个实例具有 children,我可以通过这样的名称访问 children:
let bins = new Bins(proj);
console.log(bins('Videos')('More')('NiteLite_90.mp4'));
// Return: {name: 'NiteLite_90.mp4', children: []}
使用下面的代码,我可以获得两个级别的深度(低至 'More'),但不能超过那个级别。我希望能够无限深入。
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
我能得到一些设置方面的帮助吗? 我已经研究了这里的多个其他 currying 帖子并看到了几个关于它的博客,但我仍然不明白,我需要一些关于我的代码的具体帮助。
这是您要找的东西吗?
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)
小解释
柯里化的整个思想是函数 return 函数。所以你通常会做这样的事情:
const hello = (name, age, job) => (
`My nam is ${name}, I am ${age} and I do ${job} for a living`
)
用柯里化你会做这样的事情:
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'))
当您拥有复杂的高阶函数时,柯里化的好处真正发挥作用。
只需创建一个函数,returns 本身:
class Foo{
constructor(){
function bar(){
//Do something nice here
return bar
}
this.bar=bar
}
}
new Foo().bar(1)(2)(3)
现在,你可以无限深入...
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
好的,但是如何收集所有通话的数据?
这是开始变得棘手的地方。
- 您可以在每次调用函数的 'owner' 对象后存储一些东西:
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]
- 或者,通过
bind
ingthis
将一些信息传递给返回的函数,如下所示:
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]
你可以有一个递归的 curry 函数来达到你想要的深度。但是你还有一个额外的问题:你怎么知道什么时候停止 return 函数并 return 实际对象?
如果你调用 bins.bin('Video')('More')
-- 你怎么知道你是想 return More
对象的 bin 还是搜索 [=15 的子对象的函数=] 所以你可以找到 'NiteLite_90.mp4
bin?
以下是一种可能的解决方案,可为您提供两种选择:
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
}
现在您可以深入无限数量的级别,并且可以通过 .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" }
与您原来的方法一样,search
方法可以 return 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
所以为了安全起见,您可能希望将此类调用包装在 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
}