我最近遇到了一个有趣的问题.我正在开发一些现有的代码,无论好坏,通常有几个级别的原型继承以各种各种对象的构造函数结尾.这些中的每一个最终都来自单个基础“类”.
每个构造函数都遵循以下继承模式,其中Sub是子类,Super是它的直接原型:
function Sub(/*args*/) {
Super.call(this,/*args*/);
}
Sub.prototype = Object.create(Super.prototype);
这种模式一直持续到单个基类.
发现我们需要为原型链上的每个对象存储构造函数名称列表非常有益,我想出了基类的以下代码添加,它使用了arguments对象的被调用者和调用者属性:
var curConstructor = arguments.callee;
this.constructorList = [curConstructor.name];
while(curConstructor.caller && curConstructor.caller.name !== ''){
curConstructor = curConstructor.caller;
this.constructorList.push(curConstructor.name);
}
这个问题的主要问题是它只在对象是在匿名函数(或全局范围)内构造时才有效.例如,假设我有一个构造函数SubA1 with原型SubAwith原型Base,如果我这样做:
function() {
var aOk = new SubA1();
function oops() {
var aNotOk = new SubA1();
}
oops();
}
aOk的构造函数列表是:[Base,SubA,SubA1],这就是我想要的.
aNotOk的构造函数列表最终为:[Base,SubA,SubA1,oops].
我可以通过让每个构造函数将它们的名称添加到构造函数列表本身来修复它,但是存在一些问题,其中最重要的是将代码添加到每个构建器的巨大痛苦(以及调试/维护噩梦).许多构造函数.
有一种优雅的方式来实现这一目标吗?
请注意,虽然上面的问题是在更广泛的意义上提出的,而不是Node.js特有的,但我实际上是在Node.js环境中使用代码.因此,欢迎特定于Node的解决方案.
更新:
在我的特定情况下,所有构造函数都在命名空间内.所以我实际上可以通过执行检查来确定函数名称是否存在于命名空间中,然后再将其添加到constructorList中来解决问题.
基本上,我可以这样做:
var curConstructor = arguments.callee;
this.constructorList = [curConstructor.name];
while(curConstructor.caller){
curConstructor = curConstructor.caller;
// Exit if the function name isn't part of the namespace
if(typeof namespace[curConstructor.name] !== 'function') break;
this.constructorList.push(curConstructor.name);
}
当然,这不会在一般意义上解决它,并且它并不完美.例如,如果调用新SubA1的函数恰好与命名空间中的一个构造函数具有相同的名称,它将抛出该列表.在我的特定情况下,由于构造函数遵循唯一的命名模式,因此这种情况极不可能发生.
我知道arguments.callee已被弃用,并且Object.constructor不值得信任(它无法得到我想要的东西),但我不知道通过修改获取链中每个构造函数的任何其他方法只是基础’类’.
最佳答案 当这些函数被注册为原型的属性时,它将获取原型链的所有构造函数:
function getConstructorChain(obj, type) {
var cs = [], pt = obj;
do {
if (pt = Object.getPrototypeOf(pt)) cs.push(pt.constructor || null);
} while (pt != null);
return type == 'names' ? cs.map(function(c) {
return c ? c.toString().split(/\s|\(/)[1] : null;
}) : cs;
}
如果没有参数,则会返回一个函数数组,其中type =’names’是一个函数名字符串数组.
工作FIDDLE with a HTMLDivElement object as example here.使用控制台.