在javascript中使用原型时设置函数名(用于日志记录)

Setting function name (for logging) when using prototype in javascript

本文关键字:用于 日志 记录 函数 设置 javascript 原型      更新时间:2023-09-26

我使用原型在javascript中定义对象。

例如

Listerly.prototype.init = function() {
    this.storage = new ListerlyStorage();
    this.mainView = new ListerlyMainView();
} 

一切都很好,但我也试图使用以下非常简单的日志记录方法来限制我的控制台日志记录:

Listerly.prototype.log = function(message) {
    if (this.loggingEnabled) {
        if (arguments && arguments.callee && arguments.callee.caller) {
            var methodName = arguments.callee.caller.name;
            console.log("Method name: "+ methodName);
            if (this.logMethods["*"] || this.logMethods[methodName]) {
                if (methodName) console.log(methodName + ":" + message);
                else console.log(message);
            }
        } else {
            console.log(message);
        }
    }
};

问题是使用原型定义的函数没有函数名,因此arguments.callee.caller.name为空。

有什么干净的方法可以设置函数名吗?我试着在完成后迭代原型,并在每个函数上设置.name字段,但这似乎不起作用。

有一个丑陋的解决方法,这样做会污染全局命名空间:

function listerly_finishedLoadingUser(user) {
    this.user = user;
    this.mainView.setUser(user);
}
Listerly.prototype.finishedLoadingUser = listerly_finishedLoadingUser;

希望有更干净的方式吗?

name属性是特殊的,在大多数JavaScript实现中不能更改其值。

自定义函数名称是ECMAScript 6讨论的一部分,请参阅strawman提案和最终提案。

在ES6兼容的浏览器中,即使使用原型函数,也应该获得预期的函数名称:

Listerly.prototype.finishedLoadingUser = function() { }
console.log(Listerly.prototype.finishedLoadingUser.name);
// prints "finishedLoadingUser"

如果你正在寻找一个适用于所有浏览器的解决方案,你可以使用谷歌的Traceur编译器将ES6源代码转换为ES5。

或者,您可以使用不同的属性来存储您的自定义函数名称:

// in the logger
var methodName = arguments.callee.caller.name ||
                 arguments.calee.caller.myCustomName;
// in the property iterator
for (var key in obj) {
  obj[key].myCustomName = key;
}

函数名称/标识符在函数表达式中是可选的:

Listerly.prototype.finishedLoadingUser =
  function listerly_prototype_finishedLoadingUser (user) {
    // …
  };

在严格模式下,可以使用函数中的函数名进行递归,其中禁止使用arguments.callee

请注意,在旧的(borken)JScript版本中,标识符也可以在函数之外使用,因此要明智地选择它。此外,标识符长度可能存在(未指定)限制。

您使用的(只读)name属性目前只是一个事实上的标准。在不可用的情况下,您可以从函数序列化中获得函数名,也就是说,将Function实例转换为字符串并提取标识符部分(就像我在JSX:object.js中所做的那样):

/**
 * Returns the name of a function
 *
 * @param {Function|String} aFunction
 * @return {string}
 *   The name of a function if it has one; the empty string otherwise.
 */
getFunctionName: function (aFunction) {
  /* Return the empty string for null or undefined */
  return (aFunction != null
           && typeof aFunction.name != "undefined" && aFunction.name)
    || (String(aFunction).match(/^'s*function's+([A-Za-z_]'w*)/) || [, ""])[1];
},

通常,函数序列化是不可靠的

还要注意,arguments对象和Function实例的caller属性是专有的。