50

这是 Google Chrome 调试器一直困扰着我的问题,我想知道是否有办法解决它。

我正在开发一个大型 Javascript 应用程序,使用大量面向对象的 JS(使用Joose框架),当我调试我的代码时,我的所有类都被赋予了一个无意义的初始显示值。要了解我的意思,请在 Chrome 控制台中尝试以下操作:

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

console.log(myObj);

输出应该是单行,您可以展开它以查看 的所有属性myObj,但您首先看到的只是▶ F.

我的问题是,由于我的 OO 框架,每个实例化的对象都有相同的 'name'。它看起来负责这个的代码是这样的:

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
4

11 回答 11

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

会做的伎俩,是最高效的解决方案。也没有评估。

于 2015-10-11T17:14:16.790 回答
15

在过去的 3 个小时里,我一直在玩这个,最后使用 new Function 至少有点优雅,正如其他线程所建议的那样:

/**
 * 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"}
于 2014-04-05T11:36:39.270 回答
12

结合使用计算属性名称来动态命名属性,并使用推断函数命名来为我们的匿名函数提供计算属性名称:

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's here 导致 JS 查找括号内的[]值,并将其用作属性名称。

推断函数命名

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

这里我们使用推断函数命名。该语言查看函数被分配到的任何位置的名称,并给出推断名称的函数。

我们可以将这两种技术结合起来,如开头所示。我们创建一个匿名函数,它通过推断的函数命名从计算的属性名称中获取它的名称,这是我们想要创建的动态名称。然后我们必须从嵌入它的对象中提取新创建的函数。


使用堆栈跟踪的示例

命名提供的匿名函数

// 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!");
});

于 2018-05-18T01:22:53.540 回答
5

虽然它很丑,但你可以通过 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));
  };
})();
于 2011-05-06T08:03:38.737 回答
2

这不会完全解决您的问题,但我建议覆盖类原型上的 toString 方法。例如:

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

如果您直接在控制台中输入 my_class 的实例,您仍然会看到原始类名(我认为对此无法做任何事情),但您会在错误消息中获得好听的名称,我发现非常有帮助。例如:

a = new my_class()
a.does_not_exist()

会报错:“TypeError: Object Name of Class has no method 'does_not_exist'”

于 2012-12-04T19:32:56.500 回答
2

如果要动态创建命名函数。您可以使用new Function创建命名函数。

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

getMutableCopy("bar",{}) 
// ▶ bar{}
于 2019-05-14T14:57:47.167 回答
1

类似于@Piercey4 的答案,但我也必须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'
于 2017-11-21T16:08:44.823 回答
0

通常你使用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

并且由于您无法评估返回语句(eval("return new name()");),我认为您被卡住了

于 2011-05-05T15:25:57.677 回答
0

我认为这是动态设置函数名称的最佳方法:

   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}
于 2015-06-11T14:18:23.063 回答
0

使用 ECMAScript2015 (ES2015, ES6) 语言规范,可以动态设置函数名称,而无需使用缓慢且不安全的eval函数,也无需使用 Object.defineProperty方法,该方法既会破坏函数对象,也不会在某些关键方面起作用。

例如,这个nameAndSelfBind函数能够命名匿名函数和重命名命名函数,以及将它们自己的主体绑定到自身作为this并存储对要在外部范围(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);
})();
于 2019-03-02T16:58:09.627 回答
0

根据@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() 是一种使它起作用的魔法。如果我排除它,就没有任何效果了。

于 2018-08-28T17:40:52.270 回答