How to dynamically set a function/object name in Javascript as it is displayed in Chrome

前端 未结 11 1459
萌比男神i
萌比男神i 2020-11-28 07:21

This is something which has been bugging me with the Google Chrome debugger and I was wondering if there was a way to solve it.

I\'m working on a large Javascript ap

相关标签:
11条回答
  • 2020-11-28 08:01

    I think this is the best way to dynamically set the name of a function :

       Function.prototype.setName = function (newName) {
           Object.defineProperty(this,'name', {
              get : function () { 
                  return newName; 
              }
           });
        }
    

    Now you just need to call the setName method

    function foo () { }
    foo.name; // returns 'foo'
    
    foo.setName('bar');
    foo.name; // returns 'bar'
    
    foo.name = 'something else';
    foo.name; // returns 'bar'
    
    foo.setName({bar : 123});
    foo.name; // returns {bar : 123}
    
    0 讨论(0)
  • 2020-11-28 08:02

    I've been playing around with this for the last 3 hours and finally got it at least somewhat elegant using new Function as suggested on other threads:

    /**
     * JavaScript Rename Function
     * @author Nate Ferrero
     * @license Public Domain
     * @date Apr 5th, 2014
     */
    var renameFunction = function (name, fn) {
        return (new Function("return function (call) { return function " + name +
            " () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
    };   
    
    /**
     * Test Code
     */
    var cls = renameFunction('Book', function (title) {
        this.title = title;
    });
    
    new cls('One Flew to Kill a Mockingbird');
    

    If you run the above code, you should see the following output to your console:

    Book {title: "One Flew to Kill a Mockingbird"}
    
    0 讨论(0)
  • 2020-11-28 08:02

    normally you use window[name] like

    var name ="bar"; 
    window["foo"+name] = "bam!"; 
    foobar; // "bam!"
    

    which would lead you to a function like:

    function getmc (object, name) { 
    
        window[name] = function () {}; 
        window[name].prototype = object; 
        return new window[name](); 
    
    }
    

    but then

    foo = function(){}; 
    foobar = getmc(foo, "bar"); 
    foobar; // ▶ window
    foobar.name; // foo
    x = new bar; x.name; // foo .. not even nija'ing the parameter works
    

    and since you can't eval a return statement (eval("return new name()");), I think you're stuck

    0 讨论(0)
  • 2020-11-28 08:04

    With ECMAScript2015 (ES2015, ES6) language specification, it is possible to dynamically set a function name without the use of slow and unsafe eval function and without Object.defineProperty method which both corrupts function object and does not work in some crucial aspects anyway.

    See, for example, this nameAndSelfBind function that is able to both name anonymous functions and renaming named functions, as well as binding their own bodies to themselves as this and storing references to processed functions to be used in an outer scope (JSFiddle):

    (function()
    {
      // an optional constant to store references to all named and bound functions:
      const arrayOfFormerlyAnonymousFunctions = [],
            removeEventListenerAfterDelay = 3000; // an auxiliary variable for setTimeout
    
      // this function both names argument function and makes it self-aware,
      // binding it to itself; useful e.g. for event listeners which then will be able
      // self-remove from within an anonymous functions they use as callbacks:
      function nameAndSelfBind(functionToNameAndSelfBind,
                               name = 'namedAndBoundFunction', // optional
                               outerScopeReference)            // optional
      {
        const functionAsObject = {
                                    [name]()
                                    {
                                      return binder(...arguments);
                                    }
                                 },
              namedAndBoundFunction = functionAsObject[name];
    
        // if no arbitrary-naming functionality is required, then the constants above are
        // not needed, and the following function should be just "var namedAndBoundFunction = ":
        var binder = function() 
        { 
          return functionToNameAndSelfBind.bind(namedAndBoundFunction, ...arguments)();
        }
    
        // this optional functionality allows to assign the function to a outer scope variable
        // if can not be done otherwise; useful for example for the ability to remove event
        // listeners from the outer scope:
        if (typeof outerScopeReference !== 'undefined')
        {
          if (outerScopeReference instanceof Array)
          {
            outerScopeReference.push(namedAndBoundFunction);
          }
          else
          {
            outerScopeReference = namedAndBoundFunction;
          }
        }
        return namedAndBoundFunction;
      }
    
      // removeEventListener callback can not remove the listener if the callback is an anonymous
      // function, but thanks to the nameAndSelfBind function it is now possible; this listener
      // removes itself right after the first time being triggered:
      document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
      {
        e.target.removeEventListener('visibilitychange', this, false);
        console.log('\nEvent listener 1 triggered:', e, '\nthis: ', this,
                    '\n\nremoveEventListener 1 was called; if "this" value was correct, "'
                    + e.type + '"" event will not listened to any more');
      }, undefined, arrayOfFormerlyAnonymousFunctions), false);
    
      // to prove that deanonymized functions -- even when they have the same 'namedAndBoundFunction'
      // name -- belong to different scopes and hence removing one does not mean removing another,
      // a different event listener is added:
      document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
      {
        console.log('\nEvent listener 2 triggered:', e, '\nthis: ', this);
      }, undefined, arrayOfFormerlyAnonymousFunctions), false);
    
      // to check that arrayOfFormerlyAnonymousFunctions constant does keep a valid reference to
      // formerly anonymous callback function of one of the event listeners, an attempt to remove
      // it is made:
      setTimeout(function(delay)
      {
        document.removeEventListener('visibilitychange',
                 arrayOfFormerlyAnonymousFunctions[arrayOfFormerlyAnonymousFunctions.length - 1],
                 false);
        console.log('\nAfter ' + delay + 'ms, an event listener 2 was removed;  if reference in '
                    + 'arrayOfFormerlyAnonymousFunctions value was correct, the event will not '
                    + 'be listened to any more', arrayOfFormerlyAnonymousFunctions);
      }, removeEventListenerAfterDelay, removeEventListenerAfterDelay);
    })();
    
    0 讨论(0)
  • 2020-11-28 08:09

    Combine usage of computed property name to dynamically name a property, and inferred function naming to give our anonymous function that computed property name:

    const name = "aDynamicName"
    const tmp  = {
      [name]: function(){
         return 42
      }
    }
    const myFunction= tmp[name]
    console.log(myFunction) //=> [Function: aDynamicName]
    console.log(myFunction.name) //=> 'aDynamicName'
    

    One could use whatever they want for 'name' here, to create a function with whatever name they want.

    If this isn't clear, let's break down the two pieces of this technique separately:

    Computed Property Names

    const name = "myProperty"
    const o = {
      [name]:  42
    }
    console.log(o) //=> { myProperty: 42 }
    

    We can see that the property name assigned on o was myProperty, by way of computed property naming. The []'s here cause JS to lookup the value inside the bracket, and to use that for the property name.

    Inferred Function Naming

    const o = {
      myFunction: function(){ return 42 }
    }
    console.log(o.myFunction) //=> [Function: myFunction]
    console.log(o.myFunction.name) //=> 'myFunction'
    

    Here we use inferred function naming. The language looks at the name of wherever the function is being assigned to, & gives the function that inferred name.

    We can combine these two techniques, as shown in the beginning. We create an anonymous function, which gets it's name via inferred function naming, from a computed property name, which is the dynamic name we wanted to create. Then we have to extract the newly created function from the object it is embedded inside of.


    Example Using Stack Trace

    Naming a supplied anonymous function

    // Check the error stack trace to see the given name
    
    function runAnonFnWithName(newName, fn) {
      const hack = { [newName]: fn };
      hack[newName]();
    }
    
    runAnonFnWithName("MyNewFunctionName", () => {
      throw new Error("Fire!");
    });

    0 讨论(0)
提交回复
热议问题