JavaScript 继承和提升

JavaScript Inheritance and Hoisting

在我当前的 Web 项目中,我正在处理多个 JavaScript 文件,每个文件都包含从其他类型继承的类型定义。所以在任何给定的文件中,我可以有类似...

function Type(){
    ParentType.call(this);
}
Type.prototype = Object.create( ParentType.prototype );
Type.prototype.constructor = Type;

... 与 ParentType 在另一个文件中类似地声明:

function ParentType(){
    this.data = "";    
}    

由于在 <head> 标签中处理许多 JavaScript 文件变得很麻烦,我写了一个 bash 脚本来将文件连接成一个文件(称为 master.js ).这样,我可以 link 我的 HTML.

中的单个文件

对于只有一个 child 的类型,原型链构建正确,无论我的文件的串联顺序如何。换句话说,这个片段 ...

function ParentType(){
    this.data = "";
}

function Type(){
    ParentType.call(this);
}
Type.prototype = Object.create( ParentType.prototype );
Type.prototype.constructor = Type;

... 与此代码段的行为相同:

function Type(){
    ParentType.call(this);
}
Type.prototype = Object.create( ParentType.prototype );
Type.prototype.constructor = Type;

function ParentType(){
    this.data = "";
}

当我在任一场景中创建 Type 的实例时,ParentType.prototype.isPrototypeOf(typeobj) returns 为真(其中 typeobj 是我的 Type 实例)。

但是,当我将另一种类型添加到 "prototype chain" 的末尾时,它仅在文件按顺序连接时有效,即:

function ParentType(){
    this.data = "";
}

function Type(){
    ParentType.call(this);
}
Type.prototype = Object.create( ParentType.prototype );
Type.prototype.constructor = Type;

function ChildType(){
    Type.call(this);
}
ChildType.prototype = Object.create( Type.prototype );
ChildType.prototype.constructor = ChildType;

... 否则,链 "breaks"。我猜测为什么这在 single-child 场景中是可以的,因为两种类型定义都被提升了,并且只有一组 statements 需要担心原型链。但是对于multi-link个原型链,如果语句乱了,链就无法正常连接。

所以我真正想问的是,有没有一种方法可以在 JavaScript 中实现继承 "works" 而不管我的文件的连接顺序如何? 虽然我的第一个想法是 classextends 做事的方式,但后来我了解到甚至 class 定义都没有提升!

注意:"works",我的意思是子类型从(全部)它们的 parents 继承 functions/values,并且 isPrototypeOf returns 对任何 [=65] 都是正确的=] 当检查它的任何 parents' 原型时。

函数被提升了,所以它们可能是乱序的,但是链接原型的调用必须是有序的。

如果它们乱序,托管后您的代码将如下所示。

function ParentType(){
  this.data = "";
}

function Type(){
    ParentType.call(this);
}

function ChildType(){
    Type.call(this);
}
ChildType.prototype = Object.create( Type.prototype );
ChildType.prototype.constructor = ChildType;

Type.prototype = Object.create( ParentType.prototype );
Type.prototype.constructor = Type;

也就是说,您将 ChildType.prototype 链接到 Type.prototype,然后覆盖 Type.prototype 以将其链接到 ParentType.prototype

没有办法让这项工作乱序,JavaScript继承取决于按顺序调用的那些代码行。

一种方法是为每种类型声明一个静态初始值设定项。

然而,这只能在运行时执行(通过 Object.crate 或通过赋值),因此我们又回到了起点。如果你的文件确实是按随机顺序连接的,那么你应该使用更复杂的机制和一种 "factory" 函数来创建它们出现的类型并放置子类型 "on hold" 直到它们的父类被创建。

简化示例仅供说明:

function factory(precondition, callback)
{
     if( !this.registry ) this.registy = [];
     if( !precondition || this.registry[precondition] ) 
         this.registry[callback()] = true;
     else setTimeout(function(){factory(precondition, callback);}, 100);
}

factory('Type', function()
{
     Window.ChildType = function(){}
     return 'ChildType';
});

factory(null, function()
{
     Window.Type= function(){}
     return 'Type';
});

For multi-link prototype chains, if the statements are out of order, the chain fails to connect properly.

是的。在使用它创建 ChildType.prototype 之前,您需要设置 Type.prototype。这与提升函数声明无关。

Is there a way to implement inheritance in JavaScript that "works" regardless of the order in which my files are concatenated?

嗯,你 可以 可以使用 Object.setPrototypeOf:

function ChildType(){
    Type.call(this);
}
Object.setPrototypeOf(ChildType.prototype, Type.prototype);

function Type(){
    ParentType.call(this);
}
Object.setPrototypeOf(Type.prototype, ParentType.prototype);

function ParentType(){
    this.data = "";
}

但是,您真的很想避免这种方法,并且像这样依赖提升是一种非常糟糕的做法,所以您真的应该修复您的串联脚本以使用正确的方法命令。或者使用模块系统为您计算依赖关系。

My first though was the class and extends way of doing things, but then I learned that even class definitions aren't hoisted!

。只需将它们视为纯糖 - 您始终需要按照层次结构以正确的顺序设置它们。