簡體   English   中英

如何在 Javascript 中動態設置功能/對象名稱,因為它在 Chrome 中顯示

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

這是谷歌 Chrome 調試器一直困擾我的問題,我想知道是否有辦法解決它。

我正在開發一個大型 Javascript 應用程序,使用大量面向 object 的 JS(使用Joose框架),當我調試我的代碼時,我的所有類都被賦予了一個無意義的初始顯示值。 要了解我的意思,請在 Chrome 控制台中嘗試以下操作:

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

console.log(myObj);

output 應該是一行,您可以展開它以查看myObj的所有屬性,但您首先看到的只是▶ F

我的問題是,由於我的 OO 框架,實例化的每個 object 都獲得相同的 '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
Object.defineProperty(fn, "name", { value: "New Name" });

會做的伎倆,是最高效的解決方案。 也沒有評估。

在過去的 3 個小時里,我一直在玩這個,最后按照其他線程的建議,使用新的 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');

如果您運行上述代碼,您應該會在控制台中看到以下 output:

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

結合使用計算的屬性名稱來動態命名一個屬性,並推斷 function 命名來給我們的匿名 function 計算屬性名稱:

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

人們可以在這里使用任何他們想要的“名稱”,以創建一個 function 與他們想要的任何名稱。

如果不清楚,讓我們分別分解這項技術的兩個部分:

計算屬性名稱

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

通過計算屬性命名,我們可以看到分配給o的屬性名稱是myProperty 這里的[]導致 JS 查找括號內的值,並將其用作屬性名稱。

推斷Function命名

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

這里我們使用推斷的 function 命名。 該語言查看 function 被分配到的任何位置的名稱,並給出推斷名稱的 function。

我們可以將這兩種技術結合起來,如開頭所示。 我們創建了一個匿名的 function,它通過推斷的 function 命名從計算的屬性名稱中獲得它的名稱,這是我們想要創建的動態名稱。 然后我們必須從嵌入其中的 object 中提取新創建的 function。


使用堆棧跟蹤的示例

命名提供的匿名 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;"); });

雖然它很丑,但你可以通過 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.

注意:您只能使用有效的名稱作為 function 名稱!

附錄:為避免對每個 object 實例進行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));
  };
})();

這不會完全解決您的問題,但我建議覆蓋類原型上的 toString 方法。 例如:

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

如果您直接在控制台中輸入 my_class 的實例,您仍然會看到原始的 class 名稱(我認為對此無法做任何事情),但您會在錯誤消息中獲得好聽的名稱,我發現非常有幫助。 例如:

a = new my_class()
a.does_not_exist()

會給出錯誤信息:“TypeError: Object Name of Class has no method 'does_not_exist'”

如果要動態創建一個命名為 function。 您可以使用新的 Function來創建您命名的 function。

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

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

我認為這是動態設置 function 名稱的最佳方法:

   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}

類似於@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'

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

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.

例如,請參閱這個nameAndSelfBind function,它能夠命名匿名函數和重命名命名函數,以及將它們自己的主體綁定到自身,存儲對要在外部 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);
})();

通常你使用window[name]之類的

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

這將引導您到 function ,例如:

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()"); ),我認為您被卡住了

我還沒有看到有人提到使用 ES6 代理。 在我看來,這很好地解決了這個問題。 所以就在這里。

 function shadow(object, secondObject) { return new Proxy(object, { get(target, prop, receiver) { if (secondObject.hasOwnProperty(prop)) return secondObject[prop]; return Reflect.get(...arguments); } }) } let t=function namedFunction(a,b,c){return a+b+c;} console.log(t.name)//read only property let f=shadow(t,{name:"addition"}) console.log(f.name)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM