是否可以对继承进行建模



我这个问题的主要信息来源来自詹姆斯·肖尔(James Shore)在此地址的教程:http://www.objectplayground.com/...在重看了几次视频后,我带走了足够多的东西来串起一系列课程,但我仍然不明白引擎盖下发生了什么。James解释经典模型继承的方式(我认为它指的是使用类来组织数据,而不是旧的做事方式)的工作方式如下:

计算任何函数定义语句时,将创建两个对象。创建的第一个对象是构造函数(出于此问题的目的,我将其称为cStruct),并且可以通过其称为原型的成员来识别,根据他和许多其他来源的说法,该成员指向函数对象原型Function.prototype。但随后它变得奇怪,因为创建的第二个对象是原型(或proType),它又可以通过它自己的成员(称为构造函数)来识别,该成员指向cStruct,以及拥有自己的原型成员指向对象对象原型,或者只是Object.prototype。好吧,很简单(哈!),但随后他(和其他来源)继续;当一个类扩展另一个类时,我们放弃扩展(较新/较窄)类proType将其交换为实际上指向扩展(原始/旧/更宽)类proType的新类。

好的,所以通过这个描述,我尝试仅使用具有null原型作为构建块的对象来重新创建继承模型,以便我可以在控制台中检查结果,而不必筛选所有我还不理解的内容。基本上,我正在寻找继承的吸烟枪;证明它不仅仅是一个魔术。这似乎是可能的,但是一旦逻辑以代码形式建立,它就无法提供继承的属性也就不足为奇了。你会明白我的意思。

三类---共同做法

function ExampleClass_A (  ) {  }
function ExampleClass_B (  ) {  }
function ExampleClass_C (  ) {  }

字面解释

注意这些实际上是模拟类。它们展示了我所理解的在创作类时幕后发生的事情。在这一点上,在我们稍后开始扩展类之前,c变量几乎可以忽略

a = Object.create( null );
b = Object.create( null );
c = Object.create( null );  // this represents the Object Prototype
cStruct_A               = a;
proType_A               = b;
proType_A.prototype     = c;
cStruct_A.prototype     = b;
proType_A.constructor   = a;
delete a; delete b;
a = Object.create( null );
b = Object.create( null );
cStruct_B               = a;
proType_B               = b;
proType_B.prototype     = c;
cStruct_B.prototype     = b;
proType_B.constructor   = a;
delete a; delete b;
a = Object.create( null );
b = Object.create( null );
cStruct_C               = a;
proType_C               = b;
proType_C.prototype     = c;
cStruct_C.prototype     = b;
proType_C.constructor   = a;
delete a; delete b; delete c;

线性类扩展---常见做法

编辑 @Vog,我不确定对象组合是什么,但如果我不得不猜测,我会说这是我在这个问题中创作的线性模式的类扩展/实例化的替代模式,其中我必须假设某种形式的继承无论如何都在发生。当然,我不准备接受一种模式而不是另一种模式,因为我只是想了解使这些模式首先可行的机制是如何工作的。

ExampleClass_B.prototype = Object.create( ExampleClass_A.prototype );
ExampleClass_C.prototype = Object.create( ExampleClass_B.prototype );

字面解释

注意如果我理解正确,要使继承工作,主类之后的所有子类或彼此之间的proType必须指向其父类的proType。因此,在这里我们取消了三个类中的两个proType的类,并将它们替换为已指向适当"后续"位置的类。

a                       = Object.create( null );
a.prototype             = proType_A;
a.constructor           = cStruct_B;
alt_proType_B           = a;
cStruct_B.prototype     = alt_proType_B;
delete proType_B;
delete a;
a                       = Object.create( null );
a.prototype             = alt_proType_B;
a.constructor           = cStruct_C;
alt_proType_C           = a;
cStruct_C.prototype     = alt_proType_C;
delete proType_C;
delete a;

实例化---常见做法

var exampleInstance_W = new ExampleClass_A();   //  
var exampleInstance_R = new ExampleClass_B();   //  randomly chosen names
var exampleInstance_T = new ExampleClass_C();   //  

字面解释

a = Object.create( null );
a.prototype             = proType_A;
nstance_W               = a;
delete a;
a = Object.create( null );
a.prototype             = alt_proType_B;
nstance_R               = a;
delete a;
a = Object.create( null );
a.prototype             = alt_proType_C;
nstance_T               = a;
delete a;

常见做法---作业

ExampleClass_A.prototype.prop_a = "test a,";
ExampleClass_B.prototype.prop_b = "test b,";
ExampleClass_C.prototype.prop_c = "test c";

字面解释

注意这是我开始看到可能出现问题的地方

cStruct_A.prototype.prop_a = "test a,";
cStruct_B.prototype.prop_b = "test b,";
cStruct_C.prototype.prop_c = "test c";
//  proType_A.prop_a = "test a";    //
//  proType_B.prop_b = "test b";    //  Kind of cool that this works
//  proType_C.prop_c = "test c";    //

测试---常见做法

console.log(
exampleInstance_T.prop_a,  //  successful log
exampleInstance_T.prop_b,  //  successful log
exampleInstance_T.prop_c,  //  successful log
);

测试---字面解释

注意现在要把所有这些努力推向高潮。这些日志产生不同的结果是多么令人困惑......

console.log(
nstance_T.prototype.prop_a,     //  unsuccessful log
nstance_T.prototype.prop_b,     //  unsuccessful log
nstance_T.prototype.prop_c,     //  successful log
);
console.log(
nstance_R.prototype.prop_a,     //  unsuccessful log
nstance_R.prototype.prop_b,     //  successful log
nstance_R.prototype.prop_c,     //  unsuccessful log
);
console.log(
nstance_W.prototype.prop_a,     //  successful log
nstance_W.prototype.prop_b,     //  unsuccessful log
nstance_W.prototype.prop_c,     //  unsuccessful log
);
console.log(                        //  Kind of lame that this does't work
nstance_W.prop_a,               //  
nstance_R.prop_b,               //  I mean it makes sense when you follow the
nstance_T.prop_c,               //  established logic, but at the same time
);                                  //  it means that I'm missing something.

换句话说,如果每个文字解释代码块只是模拟其上方的通用实践代码块,为什么nstance_t无法访问.prop_a.prop_b?这与我的配置有关吗?我错过了重点吗?帮帮我伙计们。提前谢谢。

为什么nstance_t无法访问.prop_a.prop_b?这与我的配置有关吗?我错过了重点吗?

您似乎期望.prototype设置对象的继承链。它没有,它只是一个普通的属性,分配它不会做任何魔法。

对象继承的对象(或null)的实际引用是内部的,通常表示为[[原型]]。您需要调用Object.getPrototypeOf(…)才能访问它。要设置它,您可以使用Object.createObject.setPrototypeOfnew

我相信这就是您正在寻找的代码:

常见做法 - ES6

class A {}
class B extends B {}
const instance = new B

常见做法 - ES5

function A() {}
function B() {}
B.prototype = Object.create(A.prototype)
B.prototype.constructor = B
const instance = new B

字面解释(对象关系,忽略构造函数调用)

A_prototype = Object.create(Object.prototype)
A_constructor = new Function(…)
A_prototype.constructor = A_constructor
A_constructor.prototype = A_prototype
B_prototype = Object.create(A_constructor.prototype)
B_constructor = new Function(…)
B_prototype.constructor = B_constructor
B_constructor.prototype = B_prototype
instance = Object.create(B_constructor.prototype)

您现在可以在A_prototype/A_constructor.prototypeB_prototype/B_constructor.prototype上分配属性,并查看它们在instance上被继承。

虽然我赞赏你对构建这样的东西的好奇心,但请注意,继承是一种众所周知的反模式。(经典的)面向对象编程的更多方面在"今天"受到质疑(在某种意义上:过去十年,如果不是更长的话)。然而,继承是几乎每个有经验的程序员都会建议反对的东西。

这里的口头禅是尽可能始终使用对象组合而不是继承(在我看来,实际上总是如此)。

这与在JavaScript之上建模一个正确的类型系统不同。或者,建模解构和结构模式匹配。这些玩具也可能永远不会在生产中使用,但至少你会建造一些你以后真正想要"真正"使用的东西。

请注意,继承在任何语言中都没有兑现其承诺,无论是C++,Python,Ruby,PHP,Smalltalk,Java等。你甚至可以在GoF书(https://en.wikipedia.org/wiki/Design_Patterns)中找到早期的提示,这是大多数OO程序员的圣经。在那里,虽然继承没有受到直接批评,但您可以将模式的整个子部分总结为"如何用可维护的对象组合结构替换难以维护的继承结构"。

最新更新