【问题标题】:Class-based and Object-based languages comparison (ECMAScript Specification)基于类和基于对象的语言比较(ECMAScript 规范)
【发布时间】:2016-03-04 18:58:52
【问题描述】:
在基于类的面向对象语言中,一般情况下,状态由实例承载,方法由类承载,继承只是结构和行为。在 ECMAScript 中,状态和方法由对象承载,而结构、行为和状态都是继承的。

这是来自 ECMAScript 规范 2015 年 6 月的 sn-p。我不理解本文的某些部分。

  • 状态由实例承载 - 在这种情况下状态是什么意思以及这个例子(c++ 是首选)
  • 方法由类承载 - 这可能意味着,如果我想知道对象的方法,我需要查看该对象的类。
  • 继承只是结构和行为——只有结构和行为被继承?
  • 等等……

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

【问题讨论】:

    标签: javascript oop prototype ecmascript-6 django-class-based-views


    【解决方案1】:

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

    ECMAScript 6 类作为语法糖的背景

    ES6 类只是 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 或更早版本中做同样的事情很容易出现问题,但这有点离题。

    问题

    状态是由实例携带的——状态在这个上下文中是什么意思以及这个例子(c++是首选)

    类的实例是类似于 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;
    }
    

    如您所见,类的 instances 在 ES6 和 C++ 中的行为方式相同,但在 ES6 中没有 privatepublic 封装的字面等效项。

    方法由类携带——这可能意味着,如果我想知道对象的方法,我需要查看该对象的类。

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

    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
    

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

    继承只是结构和行为——只有结构和行为被继承?

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

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

    【讨论】:

      【解决方案2】:

      在基于类的面向对象语言中,一般情况下,状态由实例承载,方法由类承载,继承只是结构和行为。在[JavaScript]中,状态和方法是由对象承载的,而结构、行为和状态都是继承的。

      面向对象系统是我们将程序和数据放在一起的系统 - 这样处理一段数据的方式与数据本身保持在一起,封装 em> 一起在一个对象中。

      通常有两种面向对象的语言。 基于类的语言原型语言。在基于类的语言中,对象的行为以及它们将持有的数据定义中定义或类定义。除此之外,这些课程几乎没有做任何工作。对象被创建为类的小工蜂副本,它们按照类定义的方式进行操作。这些实例对象完成了大量的实际工作。

      在原型语言中,没有单独的东西来定义对象的功能。有问题的对象拥有它自己的定义。

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

      Methods 只是“对象响应消息的方法”的缩写。

      如果面向对象的最初开发者谈论“对象响应消息的方式”,那么现在我们会谈论“方式”。 (如果他们说他们创建了特定类别的事物,那么我们现在将拥有面向事物的开发,我们将拥有基于类别的面向事物的语言和基于原型的面向事物的语言。关键是,不要不要被语言挂断。但我离题了)。

      所以...让我们举一些例子。现代面向对象来自施乐公司的帕洛阿尔托研究中心(称为 PARC)。所以我会使用那里的例子。

      Smalltalk 是面向对象语言的王者。 (Simula 在这个领域做了一些开创性的工作,并且对 Smalltalk 的早期版本产生了很大的影响,甚至到了 Smalltalk-72。但是,正如我们今天所知道的,Smalltalk 和面向对象,真的是在 Smalltalk-80 中出现的)。

      Smalltalk 是一种基于类的语言。

      您定义一个类。比如说,“人”类。我们需要存储一些数据。让我们使用“名字”、“姓氏”、“出生日期”和“全名”。 这些定义存储在类中。

      我们还将定义一个消息协议 - 类 Person 的对象知道如何响应的消息列表。然后我们定义一个响应每条消息的方法。 这些定义存储在类中。当aPerson(即ClassPerson的实例,与属于类Person的对象相同,名称为aPerson)需要响应消息时,在类中查找响应方法定义。 (该类本身是 Class Metaclass 的对象实例,但我们将在这个答案中远离那个兔子洞)。

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

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

      让我们定义一些方法。

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

      这个方法是在类中定义的。它将由类本身执行。所以它被称为类方法

       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 对象。这个新对象属于特定的对象类。

      我们显然需要一个可以设置类 Person 对象的名字的消息。

      我们在类 Person 中定义了这个方法。但它只会被 Class Person 的 instances 使用。

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

      self 是收到消息的对象。当对象需要响应称为named 的消息时,该对象会查找作为类 Person 定义的一部分存储的这段代码。

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

      我们为每个将数据存储在对象中的变量定义了标准的getterssetters(又名accessors)。

      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 的派生属性。我们将处理它有点不同,给它一个额外的方法

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

      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'”的结果赋值给一个ArbitraryPerson)

      所以,如果我们执行这三个语句,我们最终会创建 Person 类的三个实例。

      它们的实例变量的状态 - 即它们的属性已设置为的值 - 存储在对象中。方法代码存储在类中。但是每个对象都只有一个类,所以这不是问题。我们总是知道要在哪个类中查找方法定义。

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

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

      我们会改变每个对象的状态。

      我们可以查看对象内部并查看数据。我们必须查看类内部以查看方法定义。我们必须查看 Class 内部才能看到数据结构定义。

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

      现在是基于原型的面向对象语言。我将使用 Xerox Parc 的另一种 OO 语言 - LambdaMoo 代码。

      这是为永久打开的对象数据库服务器创建的。想想类似 erlang 的正常运行时间。 Smalltalk 做同样的事情 - 运行和运行,基本上永远,并在系统运行时接受对系统的所有更改。

      @create $thing named "person"
      @create person named "Tom"
      @create person named "Dick"
      @create person named "Harry"
      

      我们不引用任何类。我们只是直接创建对象。

      在 Lambda 中,对象具有“动词”和“属性”。动词定义和属性定义存储在对象本身中。并且属性的状态——对象封装的数据的特定值——也存储在对象中。

      如果我们愿意,我们可以改变每一个的所有行为(我们可以覆盖它)——因为它们都只是对象。他们存储自己的方法代码。如果对象在它自己的协议中没有消息,它会询问它的父母、祖父母和曾祖父母,直到他们中的任何一个有处理消息的方法,或者你没有祖先可以询问。

      如需更全面的面向 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"
      

      这允许我们向对象发送消息,然后调用对象上的方法,从而改变对象的状态。

      Tom.setFullName
      

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

      在 Smalltalk 中,类承载方法(即方法定义)和属性定义。对象本身只携带数据的状态。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-20
        • 2010-09-07
        • 2023-03-25
        • 2016-02-24
        • 2014-09-19
        • 1970-01-01
        • 2012-12-02
        • 2013-03-04
        相关资源
        最近更新 更多