如何在Chrome中显示的Javascript中动态设置函数/对象名
谷歌Chrome调试器一直困扰着我,我想知道是否有办法解决这个问题 我正在处理一个大型Javascript应用程序,使用大量面向对象的JS(使用框架),当我调试代码时,所有类都会得到一个非感官的初始显示值。要了解我的意思,请在Chrome控制台中尝试以下操作:如何在Chrome中显示的Javascript中动态设置函数/对象名,javascript,oop,google-chrome,joose,Javascript,Oop,Google Chrome,Joose,谷歌Chrome调试器一直困扰着我,我想知道是否有办法解决这个问题 我正在处理一个大型Javascript应用程序,使用大量面向对象的JS(使用框架),当我调试代码时,所有类都会得到一个非感官的初始显示值。要了解我的意思,请在Chrome控制台中尝试以下操作: var F = function () {}; var myObj = new F(); console.log(myObj); 输出应该是一行,您可以将其展开以查看myObj的所有属性,但您首先看到的是▶ F 我的问题是,由于我的O
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
>函数foo{}
>foo.name
“福”
>foo.name=“bar”
“酒吧”
>foo.name
“foo”/通常您使用窗口[名称]
类似
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(“returnnewname()”);
),我认为您被卡住了虽然它很难看,但您可以通过eval()作弊:
注意:您只能使用作为函数名有效的名称
附录:为避免对每个对象实例化执行eval
ing,请使用缓存:
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'; }
a = new my_class()
a.does_not_exist()
如果您直接在控制台中输入my_类的实例,您仍然可以看到原始的类名(我认为这是不可能的),但是您会在错误消息中得到一个很好的名称,这对我很有帮助。例如:
my_class = function () {}
my_class.prototype.toString = function () { return 'Name of Class'; }
a = new my_class()
a.does_not_exist()
将给出错误消息:“TypeError:类的对象名称没有方法‘不存在’”在过去的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"}
我认为这是动态设置函数名称的最佳方法:
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}
这是最有效的解决方案。也没有eval。类似于@Piercey4-answer,但我必须为实例设置名称:
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'
结合使用计算属性名动态命名属性,并使用推断函数命名为匿名函数指定计算属性名:
const name = "aDynamicName"
const tmp = {
[name]: function(){
return 42
}
}
const myFunction= tmp[name]
console.log(myFunction) //=> [Function: aDynamicName]
console.log(myFunction.name) //=> 'aDynamicName'
在这里,人们可以使用他们想要的任何名称来创建一个具有他们想要的任何名称的函数
如果这还不清楚,让我们将这项技术的两部分分别分解:
计算属性名
我们可以看到,通过计算属性命名,在o
上分配的属性名称是myProperty
。此处的[]
使JS查找括号内的值,并将其用作属性名
推断函数命名
这里我们使用推断函数命名。该语言查看函数被分配到的任何位置的名称,并给出该函数的推断名称
我们可以将这两种技术结合起来,如开头所示。我们创建一个匿名函数,该函数通过推断函数命名从计算属性名(我们要创建的动态名称)获取其名称。然后我们必须从嵌入其中的对象中提取新创建的函数
使用堆栈跟踪的示例
命名提供的匿名函数
//检查错误堆栈跟踪以查看给定名称
函数runanonWithName(newName,fn){
const hack={[newName]:fn};
黑客[新名字]();
}
RunanOnWithName(“MyNewFunctionName”,()=>{
抛出新错误(“火!”);
});代码>基于@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()是一种使其工作的魔力。如果我把它排除在外,就什么都不管用了 使用ECMAScript2015(ES2015,ES6)语言规范,可以动态设置函数名,而无需使用缓慢且不安全的函数,也无需使用破坏函数对象且在某些关键方面无法工作的方法
例如,请参见此nameAndSelfBind
函数,该函数既可以命名匿名函数,也可以重命名命名函数,还可以将其自身的主体绑定到自身,并存储对要在外部作用域中使用的已处理函数的引用():
如果要动态创建命名函数。可以使用创建命名函数
函数getMutableCopy(fnName,proto){
var f=新函数(`Function${fnName}(){};return${fnName}`)()
f、 原型=原型;
返回新的f();
}
getMutableCopy(“条,{})
// ▶ bar{}
+1--好主意。不幸的是,我太担心对每个对象实例化执行评估的性能成本。添加了一个带有缓存的版本。适用于传递的数组。但我需要将构造函数传递给命名函数。请停止对此进行向下投票。。在他改变问题之前,我的答案是正确的。问题看起来像:-)它是否适用于类(es6),这是一个类型函数?Google Chrome在控制台中抛出错误“类型错误:无法重新定义属性:名称”是迄今为止最好的答案。它使用API来定义属性,比如名称
const o = {
myFunction: function(){ return 42 }
}
console.log(o.myFunction) //=> [Function: myFunction]
console.log(o.myFunction.name) //=> 'myFunction'
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);
(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);
})();