《 JavaScript的工作原理》一书

图片 大多数编程语言都源于Fortran时代的古老范例。 JavaScript专家Douglas Crockford根除了这些枯燥的根源,使我们可以思考编程的未来,将理解下一代语言的要求提升到一个新的高度。

作者从基础知识入手:名称,数字,逻辑值,字符和其他基本信息。 您不仅将学习在JavaScript中使用类型的问题和困难,还将学习如何解决它们。 然后,您将开始熟悉数据结构和函数,以了解其基础的机制,并学习如何使用高阶函数和无类的面向对象编程风格。

摘录
没有类的代码如何工作


而且您认为自己在所有课堂上都很聪明,而且免费。
约翰·列侬

面向对象编程开发中的关键思想之一是在程序各部分之间交换数据的模型。 方法的名称及其参数必须以消息的形式表示。 方法调用将消息发送到对象。 每个对象都有自己的行为特征,在接收特定消息时会表现出来。 发件人认为收件人知道如何处理邮件。

另一个好处是多态性。 识别特定消息的每个对象都有权接收它。 接下来发生什么取决于对象的专业化。 这是一个非常有成果的想法。

不幸的是,我们开始因继承而分心-继承是一种非常有效的代码重用方案。 它的重要性与开发程序时降低人工成本的能力有关。 继承建立在类似的计划上,除了一些细微差别。 我们可以说某个对象或某类对象与某些其他对象或某类对象相似,但是有一些重要的区别。 在简单的情况下,一切都很好。 应当记得,现代的OOP始于Smalltalk(一种儿童编程语言)。 随着情况变得更加复杂,继承成为问题。 它引起了阶级的强大凝聚力。 更改一个类可能会导致依赖于该类的那些类失败。 来自类的模块是无用的。

此外,我们观察到对属性而不是对象的关注度增加。 尤其要注意为每个单独的属性获取(获取方法)和分配值(设置方法)的方法,在不太成功的项目中,属性是开放的,可以在不了解对象的情况下进行更改。 有可能引入一个更成功的项目,其中隐藏属性,并且方法处理事务,而不仅仅是处理属性更改。 但是这种方法并不经常使用。

另外,类型依赖过多。 类型已成为Fortran和更高版本语言的功能,因为它们对编译器创建者很方便。 从那时起,围绕类型的神话不断发展,获得了关于类型保护程序免于错误的奢侈主张。 尽管热爱类型,但错误并没有离开日常练习。

在编译阶段尽早发现错误计算时,会尊重和赞扬类型。 发现监督的时间越早,消除监督的成本就越低。 但是,通过对程序进行适当的测试,可以很快发现所有这些错误计算。 因此,类型识别错误被归类为低成本。

类型不应该归咎于难以检测且代价高昂的错误。 他们的错不是出现由此类错误引起的问题,而是需要一些技巧。 类型会迫使我们使用晦涩,混乱和可疑的编程方法。

类型就像减肥饮食。 饮食不被指责归还和体重增加。 她也不被认为是造成痛苦或造成健康问题的原因。 节食给体重带来希望,使体重恢复健康,我们将继续吃垃圾食品。

经典继承使我们认为我们可以创建高质量的程序,而我们会犯更多的错误并应用越来越低效的继承。 如果您忽略负面表现,这些类型似乎是一次重大胜利。 好处是显而易见的。 但是,如果仔细研究这些类型,您会发现成本超过了收益。

建设者


在第13章中,我们使用了工厂-返回函数的函数。 现在,我们可以对构造函数执行类似的操作-返回包含函数的对象的函数。

让我们从创建counter_constructor开始,类似于counter生成器。 它有上下两种方法:

function counter_constructor() { let counter = 0; function up() { counter += 1; return counter; } function down() { counter -= 1; return counter; } return Object.freeze({ up, down }); } 

返回的对象被冻结。 它不能被损坏或损坏。 对象具有状态。 变量计数器是对象的私有属性。 您只能通过方法访问它。 而且我们不需要使用它。

这是非常重要的情况。 对象接口仅是方法。 他的外壳很坚硬。 我们得到最好的封装。 无法直接访问数据。 这是非常高质量的模块化设计。

构造函数是一个返回对象的函数。 参数和构造函数变量成为对象的私有属性。 它没有包含数据的公共属性。 内部函数成为对象方法。 他们将财产变成封闭的财产。 属于冻结对象的方法是打开的。

方法必须实现事务。 例如,假设我们有一个person对象。 您可能需要更改将数据存储在其中的人员的地址。 为此,您不需要单独的函数集即可更改每个单独的地址元素。 我们需要一种接收对象文字的方法,该方法能够描述需要更改的地址的所有部分。

JavaScript的绝妙想法之一就是对象文字。 这是用于聚类信息的很好的表达语法。 通过创建使用和创建数据对象的方法,可以减少方法的数量,从而提高对象的完整性。

事实证明,我们有两种类型的对象。

  • 硬对象仅包含方法。 这些对象可以保护闭包中包含的数据的完整性。 它们为我们提供了多态性和封装。
  • 软数据对象仅包含数据。 他们没有行为。 这只是函数可以使用的便捷集合。

可以认为,OOP首先是使用Kobol语言向记录添加过程,从而确保了某种行为。 我认为,将数据方法和属性相结合是向前迈出的重要一步,但不应成为最后一步。

如果必须将硬对象转换为字符串,则必须启用toJSON方法。 否则,JSON.stringify会将其视为一个空对象,忽略方法和隐藏数据(请参见第22章)。

构造函数选项


一旦我创建了一个带有十个参数的构造函数。 这很难使用,因为没人记得参数的顺序。 后来发现没有人使用第二个参数,我想将其从参数列表中删除,但这会破坏所有已经开发的代码。

如果我审慎,我将有一个将一个对象作为参数的构造函数。 它通常取自对象文字,但也可以来自其他来源,例如JSON内容。

这将提供许多好处。

  • 关键行使代码具有文档化的外观。 该代码更易于阅读,因为它告诉您调用方的每个参数是什么。
  • 参数可以按任何顺序排列。
  • 将来,您可以添加新参数而不损坏现有代码。
  • 不相关的参数可以忽略。

通常,参数用于初始化私有属性。 这样做如下:

 function my_little_constructor(spec) { let { name, mana_cost, colors, type, supertypes, types, subtypes, text, flavor, power, toughness, loyalty, timeshifted, hand, life } = spec; 

这段代码使用具有与spec相同名称的属性来创建和初始化15个私有变量。 如果spec没有相应的属性,则会初始化一个新变量,该变量的值未定义。 这使您可以使用默认值填写所有缺少的值。

组成


JavaScript的生动表现力和有效性使您可以用经典范例创建程序,尽管该语言不适用于经典范例。 JavaScript还允许进行改进。 我们可以使用功能组合。 因此,除了添加一些例外之外,您还可以获取其中的一部分。 构造函数具有以下一般外观:

 function my_little_constructor(spec) { let {} = spec; const _ = other_constructor(spec); const  = function () { //   spec, , _,  }; return Object.freeze({ , _. }); } 

您的构造函数可以根据需要调用任意数量的其他构造函数,以访问状态管理及其提供的行为。 您甚至可以将完全相同的spec对象传递给它。 通过记录spec参数,我们列出了my_little_constructor所需的属性和其他构造函数所需的属性。

有时,您可以简单地将结果方法添加到冻结的对象中。 在其他情况下,我们有调用接收方法的新方法。 这样可以确保代码被重用,类似于继承,但没有强大的内聚力。 函数调用是原始的代码重用方案,没有更好的发明。

尺码


这种构造对象的方法比使用原型时要涉及更多的内存,因为每个刚性对象都包含该对象的所有方法,并且原型对象包含指向包含这些方法的原型的链接。 内存消耗的差异是否显着? 将差异与增加内存量方面的最新成就进行比较,我们可以说:不。 我们习惯于以千字节为单位读取内存。 现在我们以GB为单位进行考虑。 在这种背景下,根本没有感觉到差异。

可以通过改进模块化来减少差异。 对事务而不是属性的强调使您可以减少方法的数量,同时改善连接性。

经典模型的特点是均匀性。 每个对象必须是一个类的实例。 JavaScript消除了这些限制。 并非所有对象都需要遵守这些严格的规则。

例如,我认为使用方法将点定为刚性对象毫无意义。 点可以是两个或三个数字的简单容器。 点被传递到能够投影或插值的功能,或可以用点完成的其他功能。 这比将点赋予子类特殊的行为要高效得多。 让功能起作用。

»这本书的更多信息可以在出版商的网站上找到
» 目录
» 摘录

小贩优惠券可享受25%的折扣-JavaScript

支付纸质版本的书后,就会通过电子邮件发送电子书。

Source: https://habr.com/ru/post/zh-CN455698/


All Articles