哈Ha!
到目前为止,在我看来,我只是翻译了英语作家的有趣文章。 现在是时候自己写点东西了。 在第一篇文章中,我选择了一个肯定会对希望成长为“中级”的初级开发人员有用的主题,因为 它将根据OOP分析JavaScript和经典编程语言(C ++,C#,Java)之间的相似性/差异。 因此,让我们开始吧!
范式的一般规定
如果我们查看
Wikipedia上JavaScript的定义,我们将看到以下概念:
JavaScript(/dʒɑːvmultiskrɪpt/;缩写JS /ˈdʒeɪ.ɛs./)是一种多范式编程语言。 支持面向对象,命令式和功能性样式。 它是ECMAScript语言(ECMA-262标准)的实现。
根据此定义,JavaScript本身并不存在,而是某些EcmaScript规范的实现。 除此之外,其他语言也实现了此规范。
EcmaScript(以下简称ES)中存在以下范例:
- 结构的
- 面向对象
- 功能性
- 势在必行
- 面向方面(在极少数情况下)
ES中的OOP在
原型组织上实现 。 来自新手开发人员的回答:“ JS中的OOP与经典语言中的OOP有何不同”。 通常,它们变得非常模糊:“类使用古典语言,而原型使用JS”。
实际上,情况要复杂一些。 在行为方面,
动态类组织和
原型组织之间的差异很小(它确实存在,但并不那么全局)。
看一下Python或Ruby。 在这些语言中,OOP基于动态的类组织。 在这两种语言中,我们都可以随着程序的进行而动态地更改对象的类,并且该类中的更改也会动态影响其生成的实体。 就像在JS中一样,但在JS中,OOP基于原型。
具有静态类组织和原型组织的语言之间存在显着差异。 本身的区别是“存在类。 这里的原型”不是那么重要。
静态类组织基于什么?
这种类型的OOP的基础是“类”和“本质”的概念。
类是它可以生成的某些特定形式化的实体特征的广义集合。 即 这是由它生成的所有对象的特定总体计划。
特征有两种类型。 属性(实体的描述)和方法(实体的活动及其行为)。
由类生成的
实体是该类的副本,但具有初始化的属性。 如我们所见,该类严格规范了实体的描述(提供了一组严格定义的属性)及其行为(提供了一组严格定义的方法)。
这是关于JAVA的一个小例子:
class Person{ String name;
现在创建该类的实例:
public class Program{ public static void main(String[] args) { Person tom; } }
我们的
tom实体具有
Person类的所有特征,也具有其类的所有方法。
OOP范例为重用代码提供了非常广泛的可能性,
继承是这些功能之一。
一个类可以扩展另一个类,从而创建泛化-专业化关系。 在这种情况下,创建通用类(超类)的属性时,会将它们复制到子孙类的本质上,并且该方法可通过引用(根据继承的层次结构链)使用。 在静态类输入的情况下,此链是
static ,在动态类型的情况下,它在程序执行期间可能会更改。 这是最重要的区别。 我建议您现在记住这一刻。 此外,当我们进入原型组织时,答案“存在类别,然后原型”的本质将变得显而易见。
这种方法的缺点是什么?
我认为很明显:
- 从本质上讲,有些特性可能永远不会派上用场。
- 一个类不能动态地更改,添加,删除它提供给生成的实体的属性和方法,即 无法更改他的签名。
- 本质上,父级(或父级层次链)中不存在的属性或方法不能存在
- 内存消耗与继承层次结构中的链接数成正比(由于复制属性)
原型组织基于什么?
原型组织的关键概念是动态可变对象(dmo)。 DMO不需要课程。 他本人可以存储他的所有属性和方法。
设置属性的DMO时,它将检查其中是否存在该属性。 如果有一个属性,则只需对其进行分配;如果没有,则将添加属性并使用传递的值对其进行初始化。 DMO可以在程序中随意更改其签名。
这是一个例子:
我认为本学科的每个人都知道类语法已出现在ES6中,但这仅是语法糖,即 引擎盖下的原型。 上面的代码不应视为良好的编码习惯。 这无非是说明,它是以这种形式(现在所有普通人都使用ES6类)表示的,以免引起读者的困惑并强调理论概念上的差异。
如果将Tom对象输出到控制台,我们将看到对象本身仅具有_proto_链接,默认情况下始终存在该链接。 参考指向Person对象,该对象是Tom对象的原型。
原型是可以用作其他对象或其他对象可以在其中绘制属性和方法的对象的原型的对象。
对象的原型可以是任何对象,此外,对象可以在程序期间重新分配其原型。
让我们回到汤姆:
Tom.name = 'Tom';
请注意,名称,年龄属性和sayHi方法是tomSon对象的属性。 同时,我们在tomSon sayHi中显式调用sayHi原型方法,就好像它在Tom对象中一样,但实际上它不存在,并且从Person原型隐式返回,我们还对原型属性名称进行显式操作并隐式获取姓属性,我们称之为tomSon对象的属性,但实际上它不存在。 姓氏属性是通过原型中的
__proto__链接隐式提取的。
我们继续发展汤姆和他的儿子约翰的历史。
请注意,在程序执行过程中,我们更改了已创建对象的原型。 这就是
原型组织和
动态类组织的相似之处。 这就是为什么“经典语言和JavaScript有什么区别?”的答案“有类,有原型”的原因。 不太正确,表明对OOP理论及其在类和/或原型上的实现存在误解。
在原型组织中,与静态类不同,我们有机会在创建从该原型继承属性的实体之后对原型进行更改,这些更改将影响已经创建的实体。
Ben.hobbies = ['chess', 'badminton'];
这称为
原型组织委托模型或
原型继承 。
如何确定实体实施某些行为的能力?
在静态类组织中,此操作涉及检查实体是否具有实现所需行为的特定类中的成员身份。 在原型组织中,有
鸭子输入的概念。 在鸭子类型的情况下,检查实体执行特定行为的能力将意味着直接在特定时间点(即 在程序的不同部分,检查的结果可能截然相反。
原型方法的优点是什么?
不利之处是什么?
- 不太清楚
- 追踪什么是实体不想要的行为(即 原型比静态类组织更难预测
- 尽管JavaScript非常流行,但软件开发社区对它还不够熟悉。
结论
至此,我们今天结束。 我希望我能够传达这样一种思想,即古典语言和JavaScript之间的差异与是否存在类和原型是否存在无关,而是与组织的静态/动态性质有关。
当然,还没有考虑太多。 我不想写太长的文章,因此我们将在后续文章中讨论原型组织中的Cascade模型的功能以及OOP工具(多态,封装,抽象等)。