Class 基于和基于对象的语言比较(ECMAScript 规范)

Class-based and Object-based languages comparison (ECMAScript Specification)

In a class-based object-oriented language, in general, state is carried by instances, methods are carried by classes, and inheritance is only of structure and behaviour. In ECMAScript, the state and methods are carried by objects, while structure, behaviour, and state are all inherited.

这是 ECMAScript 规范 2015 年 6 月的片段。我不理解这段文字的部分内容。

谁能详细解释一下?例子会很棒。

好的,我会尝试回答这个问题。

ECMAScript 6 的背景知识class作为语法糖

ES6 classes 只是 ECMAScript(又名 JavaScript)中普通原型继承的语法糖。

class Foo {
    constructor(a, b) {
      this.c = a;
      this.d = b;
    }

    static bar() {}

    another() {}
}

这相当于:

function Foo(a, b) { 
  this.c = a; 
  this.d = b; 
}

Foo.bar = function() {};

Foo.prototype = {
  another: function() {}
};

虽然在 ES6 中进行继承比旧方法容易得多:

class Foo extends Bar {
    ...
}

在 ES5 或更早版本中执行相同操作很容易出现问题,但这有点离题。

问题

State is carried by instances - what does state mean in this context and example of this (c++ is preferred)

class 的实例是类似于 C++ 的对象。

class Foo {
  constructor(a) {
    this.value = a;
  }

  getValue() {
    return this.value;
  }
}

var foo = new Foo(42);
console.log(foo.getValue()); // prints 42

C++ 中的等价物:

class Foo {
private:
  int value;  
public:
  Foo(int a) : value(a) {}

  int getValue() {
    return value;
  }
};

void main() {
  Foo foo = new Foo(42);
  std::cout << +foo.getValue() << std::endl;
}

如您所见,class 的 实例 在 ES6 和 C++ 中的行为方式相同,但是没有 private 的字面等价物或 public 封装在 ES6 中。

Methods are carried by classes - this probably means, that if I want to know object's methods, I need to look at the class of that object.

在 ECMAScript 中,您可以覆盖 class 的实例上的函数,因为它和其他所有对象一样只是一个对象。

var foo = new Foo(42);
foo.getValue = function() { return this.value + 1; };
console.log(foo.getValue()); // returns 43
console.log(foo.constructor.prototype.getValue.call(foo)); // returns 42

使用对象的 .constructor.prototype 获取使用 new 关键字实例化的对象的原型。

Inheritance is only of structure and behaviour - only structure and behaviour is inherited?

他们选择了一个繁琐的措辞。我相信他们的意思是,在 ECMAScript 中,class 的实例只是另一个对象,就像其他任何东西一样,您几乎可以修改它的所有内容,而在 C++ 中,class 的实例具有更严格的要求。您不能向实例添加新方法,不能添加新属性,也不能打破语言为您提供的约束。

我希望这能回答您的问题。如果有什么不明白的,请评论,我会编辑它。

In a class-based object-oriented language, in general, state is carried by instances, methods are carried by classes, and inheritance is only of structure and behaviour. In [JavaScript], the state and methods are carried by objects, while structure, behaviour, and state are all inherited.

Object-oriented 系统是我们将程序和数据放在一起的系统 - 因此处理数据的方式与数据本身保持在一起, 封装一起在一个object.

通常有两种 object-oriented 语言。 Class-based 语言 原型语言。在 Class-based 语言中,object 的 行为 ,以及它们将持有的数据 定义 ,在 ClassClass 定义 中定义。 Classes 做的工作很少。 Objects 被创建为 Class 的小工蜂副本,并且它们按照 Class 定义的方式进行操作。这些 实例 objects 完成了大部分实际工作。

在原型语言中,没有单独的东西来定义 object 的作用。有问题的 object 拥有自己的定义。

状态只是一种表达数据的奇特方式。更准确地说,它是 object 的 属性 在特定给定时刻的值。

Methods 只是 "the object's method of responding to a message" 的缩写。

如果 object-orientation 的最初开发者谈论过 "the object's way of responding to a message",那么我们现在会谈论 "ways"。 (如果他们说他们创造了特定类别的事物,那么我们现在就会有 Thing-Oriented 发展,我们会有 Category-based thing-oriented 语言和 prototype-based thing-oriented 语言。关键是,不要挂在语言上。但我离题了)。

所以...让我们举一些例子。 Modern Object-orientation 来自 Xerox Corporation 的 Palo Alto Research Center(称为 PARC)。所以我将使用那里的示例。

Smalltalk 是 object-oriented 语言之父。 (Simula 在这个领域做了一些开创性的工作,对 Smalltalk 的早期版本产生了很大的影响,甚至影响到 Smalltalk-72。但是 Smalltalk 和 object-orientation,正如我们今天所知,真的是随着 Smalltalk-80 一起出现的).

Smalltalk 是一种 Class-based 语言。

你定义一个Class。比如说,Class 'Person'。我们需要存储一些数据。让我们使用 "Given name"、"Family name"、"Date of Birth" 和 "Full name"。 这些定义存储在 Class 中。

我们还将定义一个消息协议 - object 的 Class 人知道如何回复的消息列表。然后我们定义一个方法来响应每条消息。 这些定义存储在 Class 中。当aPerson(即ClassPerson的实例,与属于ClassPerson的object相同,名称为aPerson)需要响应时对于消息,它会在 Class 定义中查找响应方法。 (class 本身是 Class Metaclass 的 object 实例,但我们将在这个答案中远离 rabbit-hole。

所以,你写代码。您将其存储在 Class 个人中。
您定义变量。您将每个变量的数据结构定义存储在 Class Person.

您创建了一个属于 Class 的 object。我们称它为 aPerson。数据将存储在 aPerson 中。数据的格式和结构在 Person Class 中定义。

让我们定义一些方法。

首先,我们编写一个方法来创建一个 ArbitraryPerson object

此方法在 Class 中定义。它将由 Class 本身执行。所以它被称为class方法.

 Person >> called: aString
     "A Class method to return a newly created instance of Class Person, 
      with a first name set to aString"

      ^ super new givenName: aString  

"send the message new to the superclass of this class 
     - i.e. its parent in the Class hierarchy.
Send the keyword message 'named: aString' 
to the new object that was created.
Return the new object."

所以我们的第一个方法是 Class 方法,它创建一个新的 Person object。这个新的 object 属于 object 的特定 Class。

我们显然需要一条消息来设置 Class 人的 object 的名字。

我们在ClassPerson中定义了这个方法。但它只会被 Class 人的 个实例 使用。

Person >> named: aFirstNameString
    "Set the givenName variable of this object to aString.
     Return the object"

    ^self givenName: aFirstNameString

self 是收到消息的object。 object 在需要响应称为 named.

的消息时查找这段代码,该代码存储为 Class Person 定义的一部分

aFirstNameString 只是我们传递给方法的 参数 参数 的占位符。

我们为每个变量定义了标准的 getterssetters(又名 accessors)它的数据存储在 object.

Person >> givenName: aString
    "Set the givenName variable of this object to aString"

     givenName := aString

Person >> givenName
    "return the givenName of the receiver"

    ^givenName 

我们可以对其他实例变量执行相同的操作:familyNamedateOfBirthfullName

fullNamePerson 的派生属性。我们将以不同的方式处理它,通过 also 给它一个额外的方法

因为它是派生的,所以我们需要一条额外的消息来派生它。

Person setFullName
     "Calculate the full name from givenName and familyName.
     (Concatenate together using the string concatenation operator , )
      Return the receiver"

    ^self fullName := self givenName, ' ', self familyName

所以让我们使用这些方法。

 anArbitraryPerson := Person named: 'Tom'.
 anotherArbitrary := Person named: 'Dick'.
 aThirdArbitraryPerson := Person named: 'Harry'.

(读作:将调用方法“Person named: 'Tom'”的结果分配给 anArbitraryPerson )

所以,如果我们执行使用这三个语句,我们最终创建了 Person Class 的三个实例。

它们的实例变量的状态——即它们的属性设置的值——存储在object中。方法代码存储在Class中。但是每个 object 恰好只有一个 Class,所以这不是问题。我们总是确切地知道Class到look-up中的方法定义。

如果我们要发送这些消息:

anArbitraryPerson familyName 'Jones'.
anotherArbitrary familyName 'Emery'.
aThirdArbitraryPerson := Person named: 'Windsor'.

我们会改变每一个 object 的状态。

我们可以查看 object 内部并查看数据。我们必须查看 Class 内部以查看方法定义。我们必须查看 Class 内部以查看数据结构定义。

您可以在 Beginning to Smalltalk (link) 和许多其他优秀的 Smalltalk 网站上找到更多资源。

所以现在 prototype-based object-oriented 语言。我将使用 Xerox Parc 的另一个 OO-language - LambdaMoo 代码。

这是为 permanently-on object 数据库服务器创建的。想想 erlang-like 正常运行时间。 Smalltalk 做同样的事情 - 运行ning 和 运行ning,基本上是永远的,并且在 运行ning.

期间接受对系统的所有更改
@create $thing named "person"
@create person named "Tom"
@create person named "Dick"
@create person named "Harry"

我们没有提到任何 class。我们只是直接创建 objects。

在 Lambda 中,objects 有“verbs”和“properties”。动词定义和 属性 定义存储在 object 本身中。属性的状态 - object 封装的数据的特定值 - 也存储在 object 中。

如果需要,我们可以更改每一个的所有行为(我们可以覆盖它)- 因为它们都是 object。他们存储自己的方法代码。如果对象在它自己的协议中没有消息,它会询问它是 parents 和 grandparents 和 great-grandparents,直到它们中的任何一个有处理消息的方法,要不你运行出来问问老祖宗。

有关更详细的 OO 初学者教程 Yib's Pet Rock - A Programming Primer for Beginners. (link) Part of Yib's Guide to Mooing

更好的方法可能是:

@create $thing named "aPerson"
@describe aPerson as "A generic person"
@prop aPerson.givenName
@prop aPerson.familyName
@prop aPerson.fullName
@prop aPerson.dateOfBirth
@verb aPerson.setFullName [code]

@set Tom.familyName to "Jones"

这允许我们将消息发送到 object,然后调用 object 上的方法,然后更改 object 的状态。

Tom.setFullName

现在我们有了 和 object 携带的方法,以及属性定义。以及数据的状态。

在 Smalltalk 中,classes 包含方法(即方法定义)和属性定义。 object本身只携带数据的状态。