Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

185
Views
Can't access function attribute inside decoration function

In the following code I am trying to add decorators to a function. For certain reasons I would like to display the function attribute "name". However, I have no access to it as soon as I am in the individual functions. Also, I'm not sure why the functions are called from the bottom up. What are the reasons for all of the points mentioned and how can I avoid them?

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

about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

The first time you make a decorated function for a given function, that returned function does not have a name -- it is anonymous. So when you then pass that decorated function to be decorated again, fn will be that anonymous function.

To solve this, assign the name of the fn function also to the returned decorated function. That way the name will stick even when you decorate that function again, and again...

Here is a helper function that will assign the name property to a given function:

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.

Because in your decorator the last step is to call fn.

That fn might be an already decorated function, and so it is normal that earlier decorations of the function run later.

It is like wrapping a birthday present several times, each time with a different color of wrapping paper. When your friend unpacks it, they will get to see the colors of wrapping paper in the reverse order from the order in which you had applied them.

about 4 years ago · Juan Pablo Isaza Report

0

So you want to do something additional with your decorators? They need some common behavior? We've shown we know how to do that already: with decorators. Your decorators need decorators of their own!

Here I write a decorator-decorator keep which takes a decorator function and returns a new decorator function which keeps the name and length properties of the function passed to it. (Say that five times fast!)

It uses the same technique as the answer from trincot, but is less intrusive, as you can simply wrap the decorator functions just as you would the underlying ones. Here I do that at definition time, since we never really want these decorators without this behavior, but you can do it as you like.

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}

The name keep was originally keepName before I realized that I personally would also want the function to keep the arity intact. I couldn't come up with a clear useful name for this... which is a large warning sign to me. So there may be something still wrong with this design.

about 4 years ago · Juan Pablo Isaza Report

0

It's not from bottom to top, it's the order you set.

With your decorators, you basically just did this:

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

Which means first it executes requireIntegers by passing it as input everything else (countParams(rectangleArea(20, 30, "hey"))).

Then you see the console log and the error as params.forEach scans the params and finds 'hey' which is not a number.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!