Подтвердить что ты не робот

Как динамически установить имя функции/объекта в Javascript, поскольку оно отображается в Chrome

Это то, что прослушивало меня с помощью отладчика Google Chrome, и мне было интересно, есть ли способ его решить.

Я работаю над большим Javascript-приложением, используя множество объектно-ориентированных JS (используя Joose), и когда Я отлаживаю свой код, все мои классы получают нечувствительное начальное значение отображения. Чтобы узнать, что я имею в виду, попробуйте это в консоли Chrome:

var F = function () {};
var myObj = new F();

console.log(myObj);

Вывод должен состоять из одной строки, которую вы можете развернуть, чтобы увидеть все свойства myObj, но первое, что вы видите, это просто ▶ F.

Моя проблема в том, что из-за моей инфраструктуры OO каждый экземпляр объекта получает одно и то же имя. Код, который, по его мнению, отвечает за это, выглядит так:

getMutableCopy : function (object) {
    var f = function () {};
    f.prototype = object;
    return new f();
}

Это означает, что в отладчике исходное представление всегда ▶ F.

Теперь я действительно не хочу ничего менять о том, как Joose создает объекты (getMutableCopy...?), но если бы я мог добавить к этому, чтобы я мог указать свое имя, это было бы большой.

Некоторые вещи, на которые я смотрел, но не могли ни с чем:

> function foo {}
> foo.name
  "foo"
> foo.name = "bar"
  "bar"
> foo.name
  "foo"    // <-- looks like it is read only
4b9b3361

Ответ 1

Object.defineProperty(fn, "name", { value: "New Name" });

Будет делать трюк и является наиболее эффективным решением. Также нет eval.

Ответ 2

Я играю с этим в течение последних 3 часов и, наконец, получил его, по крайней мере, несколько элегантно, используя новую функцию, как предложено в других потоках:

/**
 * 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');

Если вы запустите указанный выше код, вы увидите следующий вывод на консоль:

Book {title: "One Flew to Kill a Mockingbird"}

Ответ 3

Хотя это некрасиво, вы можете обманывать через eval():

function copy(parent, name){
  name = typeof name==='undefined'?'Foobar':name;
  var f = eval('function '+name+'(){};'+name);
  f.prototype = parent;
  return new f();
}

var parent = {a:50};
var child = copy(parent, 'MyName');
console.log(child); // Shows 'MyName' in Chrome console.

Остерегайтесь: вы можете использовать только имена, которые были бы действительны как имена функций!

Добавление: Чтобы избежать eval для каждого экземпляра объекта, используйте кеш:

function Cache(fallback){
  var cache = {};

  this.get = function(id){
    if (!cache.hasOwnProperty(id)){
      cache[id] = fallback.apply(null, Array.prototype.slice.call(arguments, 1));
    }
    return cache[id];
  }
}

var copy = (function(){
  var cache = new Cache(createPrototypedFunction);

  function createPrototypedFunction(parent, name){
    var f = eval('function '+name+'(){};'+name);
    f.prototype = parent;
    return f;
  }

  return function(parent, name){
    return new (cache.get(name, parent, typeof name==='undefined'?'Foobar':name));
  };
})();

Ответ 4

Это не полностью решит вашу проблему, но я бы предложил переопределить метод toString в прототипе класса. Например:

my_class = function () {}
my_class.prototype.toString = function () { return 'Name of Class'; }

Вы все равно увидите исходное имя класса, если вы введете экземпляр my_class прямо в консоль (я не думаю, что это можно что-то сделать), но вы получите красивое имя в сообщениях об ошибках, который я считаю очень полезным. Например:

a = new my_class()
a.does_not_exist()

Выдает сообщение об ошибке: "TypeError: Имя объекта класса не имеет метода" does_not_exist ""

Ответ 5

Объедините использование имени вычисляемого свойства, чтобы динамически присвоить имя свойству, и предполагаемое именование функции, чтобы дать нашей анонимной функции, которая вычислила имя свойства:

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

Здесь можно использовать все, что они хотят для "имени", чтобы создать функцию с любым именем, которое они хотят.

Если это не ясно, давайте разберем две части этой техники отдельно:

Имена вычисляемых свойств

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

Мы можем видеть, что имя свойства, назначенное для o было myProperty, путем вычисления именования свойств. [] Здесь заставляет JS искать значение в скобках и использовать его для имени свойства.

Наименование предполагаемой функции

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

Здесь мы используем выведенную функцию именования. Язык смотрит на имя того места, которому назначается функция, и дает функцию, которая вывела название.

Мы можем объединить эти две техники, как показано в начале. Мы создаем анонимную функцию, которая получает имя через логическое имя функции из вычисляемого имени свойства, которое является динамическим именем, которое мы хотели создать. Затем мы должны извлечь вновь созданную функцию из объекта, внутри которого она встроена.

Ответ 6

Я думаю, что это лучший способ динамически установить имя функции:

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

Теперь вам просто нужно вызвать метод setName

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}

Ответ 7

Подобно @Piercey4 answer, но мне пришлось установить name для экземпляра:

function generateConstructor(newName) {
  function F() {
    // This is important:
    this.name = newName;
  };

  Object.defineProperty(F, 'name', {
    value: newName,
    writable: false
  });

  return F;
}

const MyFunc = generateConstructor('MyFunc');
const instance = new MyFunc();

console.log(MyFunc.name); // prints 'MyFunc'
console.log(instance.name); // prints 'MyFunc'

Ответ 8

Исходя из ответа @josh, он печатает в консоли REPL, показывает в console.log и показывает во всплывающей подсказке отладчика:

var fn = function() { 
   return 1917; 
};
fn.oldToString = fn.toString; 
fn.toString = function() { 
   return "That fine function I wrote recently: " + this.oldToString(); 
};
var that = fn;
console.log(that);

Включение fn.oldToString() - это магия, которая заставляет его работать. Если я исключу это, ничто больше не работает.

Ответ 9

С помощью спецификации языка ECMAScript2015 (ES2015, ES6) можно динамически устанавливать имя функции без использования медленной и небезопасной функции eval и без метода Object.defineProperty, который и повреждает объект функции, и в любом случае не работает в некоторых важных аспектах.

См., Например, эту функцию nameAndSelfBind которая может как именовать анонимные функции, так и переименовывать именованные функции, а также привязывать их собственные тела к себе и хранить ссылки на обработанные функции для использования во внешней области (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);
})();

Ответ 10

Если вы хотите динамически создать именованную функцию. Вы можете использовать новую функцию для создания вашей именованной функции.

function getMutableCopy(fnName,proto) {
    var f = new Function('function ${fnName}(){}; return ${fnName}')()
    f.prototype = proto;
    return new f();
}

getMutableCopy("bar",{}) 
// ▶ bar{}

Ответ 11

обычно вы используете window[name] как

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

который приведет вас к такой функции, как:

function getmc (object, name) { 

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

}

но затем

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

и поскольку вы не можете определить оператор return (eval("return new name()");), я думаю, что вы застряли