我正在尝试编写一个继承逻辑,在其中克隆输入类并从剩余的父类继承。为了做到这一点,我需要创建一个新类,精确地深度复制其中一个类。我正在努力实现类似的目标:
class Original {static b=1; static test2(){}; test(){}}
var CopyClass = DeepCopySomehow(Original)
class NotWorking extends Original{}
console.log(Object.getOwnPropertyNames(NotWorking))
// [ 'length', 'name', 'prototype']
console.log(Object.getOwnPropertyNames(Original))
// [ 'length', 'name', 'prototype', 'test2', 'b' ]
console.log(Object.getOwnPropertyNames(NotWorking.prototype))
// ['constructor']
console.log(Object.getOwnPropertyNames(Class.prototype))
// ['constructor', 'test']
我有类似(简化)的东西:
function inherit(Class1, Class2) {
class Base extends Class1 {...stuff...}
Object.defineProperty(Base, 'name', {value: Class1.name});
copy_props(Base.prototype, Class1.prototype);
copy_props(Base, Class1.prototype);
copy_props(Base.prototype, Class2.prototype);
copy_props(Base, Class2.prototype);
}
但是,这仍然以某种方式保留了“Base”的信息。
浏览器端
——这是一个可重现的示例:
class SpecificParentName{constructor() {return new Proxy(this, {})}}
const Base = class extends SpecificParentName{constructor(...args){super(...args)}}
Base.toString = () => SpecificParentName.toString()
Object.defineProperty(Base, 'name', {value: SpecificParentName.name});
console.log(Base)
// class extends SpecificParentName{constructor(...args){super(...args)}}
// reasonable output, although I would have wanted it to be just class SpecificParentName if possible
console.log(new Base())
// Proxy(Base) {} // definitely not desired, because it doesn't point to SpecificParentName
console.log(new Proxy(Base, {}))
// Proxy(Function) {length: 0, name: 'SpecificParentName', prototype: SpecificParentName}
// it's ok since points to SpecificParentName
-- 我之前在 Nodejs 端也遇到过类似的问题:
class SpecificParentName{}
console.log(SpecificParentName)
// "[class SpecificParentName]"
const Base = class extends SpecificParentName{}
console.log(Base)
// [class Base extends SpecificParentName]
// I'd like this^ to be just "[class SpecificParentName]"
// hacky fix on nodejs:
const Base2 = class extends SpecificParentName{
static [require('util').inspect.custom]() {return `[class ${SpecificParentName.name}]`}
console.log(Base2)
// "[class SpecificParentName]"
}
所以我的问题是,javascript 为什么以及如何知道我在打印时定义类时使用的变量名称,有没有办法自定义它?
自己的首先,每个函数(包括类构造函数)都有一个
name
属性
。其次,如何公开这个名称是函数类型特定的
实现的一部分。对于内置类型的实例,类名会通过初始
Symbol.toStringTag
实现另外公开。自定义类实现的实例不会显示后一种行为(它们的默认值始终返回
'[object Object]'
)。最后,两个实现都可以被覆盖;至于函数名称(包括类构造函数),任何分配给变量或属性的未命名/匿名函数或类表达式都会将此变量或属性的名称分配给其自己的 name
属性。
因此,OP想要实现的一切都可以通过动态子类化/子类型模式来完成,实现为创建命名子类的单个工厂函数。 该函数确实需要 3 个参数...
对于创建并返回的子类一次,
toString
Object.prototype.toString.call(subclassInstance)
处理的每个实例的实现细节
,在一定程度上确实涵盖了构造函数的功能。
class SpecificParentName {}
console.log({
SpecificParentName,
'SpecificParentName.name': SpecificParentName.name,
});
/*
* ... OP's request ...
*/
const Base = createNamedSubclass('SpecificParentName', SpecificParentName);
console.log({
Base,
'Base.name': Base.name,
});
const baseInstance = new Base;
console.log(
'Object.prototype.toString.call(baseInstance) ...',
Object.prototype.toString.call(baseInstance)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
function createNamedSubclass(
className = 'UnnamedType' ,
baseClass = Object,
initializer = (...args) => args,
) {
const subClass = ({
// - ensures the class constructor's name.
[className]: class extends baseClass {
constructor(...args) {
super(...args);
initializer.apply(this, args);
}
},
})[className];
// - responsible for exposing the constructor's intended stringification.
Reflect.defineProperty(subClass, 'toString', {
value: () => `class ${ className }{}]`,
});
// - responsible for exposing an instance' class implementation detail.
Reflect.defineProperty(subClass.prototype, Symbol.toStringTag, {
get: () => className,
});
return subClass;
}
</script>
人们可以通过阅读例如《动态子类化/子类型化》等内容来更深入地研究动态子类化/子类型化问题。