在下面的代碼中,我試圖向函式添加裝飾器。出于某些原因,我想顯示函式屬性“名稱”。但是,一旦我進入各個功能,我就無法訪問它。另外,我不確定為什么從下往上呼叫這些函式。提到的所有要點的原因是什么,我該如何避免它們?
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"));
uj5u.com熱心網友回復:
第一次為給定函式創建裝飾函式時,回傳的函式沒有名稱——它是匿名的。因此,當您再次傳遞要裝飾的裝飾函式時,fn將是該匿名函式。
為了解決這個問題,fn還需要將函式的名稱分配給回傳的裝飾函式。這樣,即使您一次又一次地裝飾該功能,名稱也會保留下來......
這是一個輔助函式,它將 name 屬性分配給給定的函式:
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可能是一個已經裝飾的函式,因此函式的早期裝飾稍后運行是正常的。
這就像多次包裹生日禮物,每次都用不同顏色的包裝紙。當您的朋友打開包裝時,他們會看到包裝紙的顏色,其順序與您使用包裝紙的順序相反。
uj5u.com熱心網友回復:
所以你想對你的裝飾者做一些額外的事情嗎?他們需要一些共同的行為嗎?我們已經展示了我們知道如何做到這一點:使用裝飾器。您的裝飾者需要自己的裝飾者!
在這里,我撰寫了一個裝飾器-裝飾器keep,它接受一個裝飾器函式并回傳一個新的裝飾器函式,該函式保留傳遞給它的函式的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在我意識到我個人也希望函式保持 arity 完整之前。我無法為此想出一個明確有用的名稱......這對我來說是一個很大的警告信號。所以這個設計可能還是有問題。
uj5u.com熱心網友回復:
不是從下到上,是你設定的順序。
使用你的裝飾器,你基本上就是這樣做的:
requireIntegers(countParams(rectangleArea(20, 30, "hey"))).
這意味著它首先requireIntegers通過將其作為輸入傳遞給其他所有內容 ( countParams(rectangleArea(20, 30, "hey"))) 來執行。
然后你會看到控制臺日志和錯誤,因為params.forEach掃描引數并發現'hey'哪個不是數字。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/335906.html
標籤:javascript 算法 装饰师
