我这个问题的主要信息来源来自詹姆斯·肖尔(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.create
、Object.setPrototypeOf
或new
。
我相信这就是您正在寻找的代码:
常见做法 - 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.prototype
和B_prototype
/B_constructor.prototype
上分配属性,并查看它们在instance
上被继承。
虽然我赞赏你对构建这样的东西的好奇心,但请注意,继承是一种众所周知的反模式。(经典的)面向对象编程的更多方面在"今天"受到质疑(在某种意义上:过去十年,如果不是更长的话)。然而,继承是几乎每个有经验的程序员都会建议反对的东西。
这里的口头禅是尽可能始终使用对象组合而不是继承(在我看来,实际上总是如此)。
这与在JavaScript之上建模一个正确的类型系统不同。或者,建模解构和结构模式匹配。这些玩具也可能永远不会在生产中使用,但至少你会建造一些你以后真正想要"真正"使用的东西。
请注意,继承在任何语言中都没有兑现其承诺,无论是C++,Python,Ruby,PHP,Smalltalk,Java等。你甚至可以在GoF书(https://en.wikipedia.org/wiki/Design_Patterns)中找到早期的提示,这是大多数OO程序员的圣经。在那里,虽然继承没有受到直接批评,但您可以将模式的整个子部分总结为"如何用可维护的对象组合结构替换难以维护的继承结构"。