无法访问装饰功能内的功能属性
Can't access function attribute inside decoration function
在下面的代码中,我尝试向函数添加装饰器。由于某些原因,我想显示函数属性“名称”。但是,我一进入各个功能就无法访问它。另外,我不确定为什么这些函数是自下而上调用的。出现上述所有问题的原因是什么?我该如何避免?
let rectangleArea = (length, width) => {
return length * width;
}
const countParams = (fn) => {
return (...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}
}
const requireIntegers = (fn) => {
return (...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}
}
//Why running from bottom to top?
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30, "hey"));
不是从下往上,是你设置的顺序。
对于你的装饰器,你基本上只是这样做了:
requireIntegers(countParams(rectangleArea(20, 30, "hey"))).
这意味着它首先执行 requireIntegers
,将其作为输入传递给其他所有内容 (countParams(rectangleArea(20, 30, "hey"))
)。
然后您会看到控制台日志和错误,因为 params.forEach
扫描参数并发现 'hey'
不是数字。
第一次为给定函数创建装饰函数时,返回的函数没有名称——它是匿名的。因此,当您再次传递 that 装饰函数时,fn
将是那个匿名函数。
要解决此问题,请将 fn
函数的名称也分配给返回的修饰函数。这样,即使您一次又一次地装饰该函数,名称也会保留...
这是一个辅助函数,它将名称 属性 分配给给定函数:
const setName = (deco, value) => {
Object.defineProperty(deco, "name", {value, writable: false});
return deco;
}
let rectangleArea = (length, width) => {
return length * width;
}
const countParams = (fn) => {
return setName((...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}, fn.name);
}
const requireIntegers = (fn) => {
return setName((...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}, fn.name);
}
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30, "hey"));
Why the functions are called from the bottom up.
因为在你的装饰器中 最后 步骤是调用 fn
.
fn
可能是一个已经修饰过的函数,所以 早期 函数的修饰 运行 以后是正常的.
就像把生日礼物包装好几次,每次都用不同颜色的包装纸。当您的朋友打开包装时,他们会看到包装纸的颜色与您使用它们的顺序相反。
所以你想用你的装饰器做一些额外的事情?他们需要一些共同的行为?我们已经展示了我们知道如何做到这一点:使用装饰器。您的装饰器需要自己的装饰器!
这里我写了一个 decorator-decorator keep
它接受一个装饰器函数和 returns 一个新的装饰器函数,它保留函数的 name
和 length
属性传给了它。 (快说五倍!)
它使用与 trincot 的答案相同的技术,但侵入性较小,因为您可以像包装底层函数一样简单地包装装饰器函数。在这里,我在定义时这样做,因为我们真的不希望没有这种行为的装饰器,但你可以随心所欲地做。
let rectangleArea = (length, width) => {
return length * width;
}
const keep = (decorator) => (fn) =>
Object .defineProperties (decorator (fn), {
name: {value: fn .name, writable: false},
length: {value: fn .length, writable: false}
})
const countParams = keep ((fn) => {
return (...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}
})
const requireIntegers = keep ((fn) => {
return (...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}
})
//Why running from bottom to top? -- answered by @balastrong
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30));
console.log(rectangleArea(20, 30, "hey"));
.as-console-wrapper {max-height: 100% !important; top: 0}
名字keep
原来是keepName
,后来我才意识到我个人也希望这个函数能保持元数不变。我无法为此想出一个明确有用的名称...这对我来说是一个很大的警告信号。所以这个设计可能还是有问题。
在下面的代码中,我尝试向函数添加装饰器。由于某些原因,我想显示函数属性“名称”。但是,我一进入各个功能就无法访问它。另外,我不确定为什么这些函数是自下而上调用的。出现上述所有问题的原因是什么?我该如何避免?
let rectangleArea = (length, width) => {
return length * width;
}
const countParams = (fn) => {
return (...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}
}
const requireIntegers = (fn) => {
return (...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}
}
//Why running from bottom to top?
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30, "hey"));
不是从下往上,是你设置的顺序。
对于你的装饰器,你基本上只是这样做了:
requireIntegers(countParams(rectangleArea(20, 30, "hey"))).
这意味着它首先执行 requireIntegers
,将其作为输入传递给其他所有内容 (countParams(rectangleArea(20, 30, "hey"))
)。
然后您会看到控制台日志和错误,因为 params.forEach
扫描参数并发现 'hey'
不是数字。
第一次为给定函数创建装饰函数时,返回的函数没有名称——它是匿名的。因此,当您再次传递 that 装饰函数时,fn
将是那个匿名函数。
要解决此问题,请将 fn
函数的名称也分配给返回的修饰函数。这样,即使您一次又一次地装饰该函数,名称也会保留...
这是一个辅助函数,它将名称 属性 分配给给定函数:
const setName = (deco, value) => {
Object.defineProperty(deco, "name", {value, writable: false});
return deco;
}
let rectangleArea = (length, width) => {
return length * width;
}
const countParams = (fn) => {
return setName((...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}, fn.name);
}
const requireIntegers = (fn) => {
return setName((...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}, fn.name);
}
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30, "hey"));
Why the functions are called from the bottom up.
因为在你的装饰器中 最后 步骤是调用 fn
.
fn
可能是一个已经修饰过的函数,所以 早期 函数的修饰 运行 以后是正常的.
就像把生日礼物包装好几次,每次都用不同颜色的包装纸。当您的朋友打开包装时,他们会看到包装纸的颜色与您使用它们的顺序相反。
所以你想用你的装饰器做一些额外的事情?他们需要一些共同的行为?我们已经展示了我们知道如何做到这一点:使用装饰器。您的装饰器需要自己的装饰器!
这里我写了一个 decorator-decorator keep
它接受一个装饰器函数和 returns 一个新的装饰器函数,它保留函数的 name
和 length
属性传给了它。 (快说五倍!)
它使用与 trincot 的答案相同的技术,但侵入性较小,因为您可以像包装底层函数一样简单地包装装饰器函数。在这里,我在定义时这样做,因为我们真的不希望没有这种行为的装饰器,但你可以随心所欲地做。
let rectangleArea = (length, width) => {
return length * width;
}
const keep = (decorator) => (fn) =>
Object .defineProperties (decorator (fn), {
name: {value: fn .name, writable: false},
length: {value: fn .length, writable: false}
})
const countParams = keep ((fn) => {
return (...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}
})
const requireIntegers = keep ((fn) => {
return (...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}
})
//Why running from bottom to top? -- answered by @balastrong
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30));
console.log(rectangleArea(20, 30, "hey"));
.as-console-wrapper {max-height: 100% !important; top: 0}
名字keep
原来是keepName
,后来我才意识到我个人也希望这个函数能保持元数不变。我无法为此想出一个明确有用的名称...这对我来说是一个很大的警告信号。所以这个设计可能还是有问题。