无法访问装饰函数内部的函数属性



在下面的代码中,我试图向函数添加装饰器。出于某些原因,我想显示函数属性";名称";。然而,我一参加个人活动就无法访问它。此外,我不知道为什么函数是从下往上调用的。上述所有观点的原因是什么?我如何避免这些观点?

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"));

第一次为给定函数生成修饰函数时,返回的函数没有名称——它是匿名的。因此,当您传递那个被修饰的函数以再次被修饰时,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"));

为什么从下到上调用函数。

因为在装饰器中,最后步骤是调用fn

fn可能是一个已经装饰过的函数,因此函数的早期装饰在后期运行是正常的。

这就像把生日礼物包了好几次,每次都用不同颜色的包装纸。当你的朋友打开包装时,他们会看到包装纸的颜色与你使用它们的顺序相反。

所以你想对你的装饰器做一些额外的事情?他们需要一些共同的行为?我们已经展示了我们知道如何做到这一点:使用装饰器。你的装饰师需要自己的装饰师!

在这里,我编写了一个decorator decoratorkeep,它接受一个decurator函数并返回一个新的decorator函数,该函数保持传递给它的函数的namelength属性

它使用了与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}

在我意识到我个人也希望函数保持arity不变之前,名称keep最初是keepName。我想不出一个明确有用的名字。。。这对我来说是一个很大的警告。所以这个设计可能仍然有问题。

它不是从下到上的,而是您设置的顺序。

对于你的装饰师,你基本上就是这样做的:

requireIntegers(countParams(rectangleArea(20, 30, "hey"))).

这意味着它首先执行requireIntegers,将其作为其他所有内容的输入(countParams(rectangleArea(20, 30, "hey")))。

然后,当params.forEach扫描参数并找到不是数字的'hey'时,您会看到控制台日志和错误。

最新更新