问题
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.
This is the snippet from ECMAScript Specification June 2015. I don't understant parts of this text.
- State is carried by instances - what does state mean in this context and example of this (c++ is preferred)
- 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.
- Inheritance is only of structure and behaviour - only structure and behaviour is inherited?
- And so on...
Can anyone please explain this in details? examples would be great.
回答1:
Okay, I'll take a shot at answering this question.
Background on ECMAScript 6 classes as syntactic sugar
ES6 classes are just syntactic sugar over the ordinary prototypal inheritance found in ECMAScript (aka JavaScript).
class Foo {
constructor(a, b) {
this.c = a;
this.d = b;
}
static bar() {}
another() {}
}
This is equivalent to:
function Foo(a, b) {
this.c = a;
this.d = b;
}
Foo.bar = function() {};
Foo.prototype = {
another: function() {}
};
Doing inheritance in ES6 is a lot easier than the old method though:
class Foo extends Bar {
...
}
Doing the same in ES5 or earlier is prone to issues, but that is slightly off-topic.
Questions
State is carried by instances - what does state mean in this context and example of this (c++ is preferred)
An instance of a class is an object similar to C++.
class Foo {
constructor(a) {
this.value = a;
}
getValue() {
return this.value;
}
}
var foo = new Foo(42);
console.log(foo.getValue()); // prints 42
Equivalent in 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;
}
As you can see, the instances of a class behave the same way in ES6 and C++, however there are no literal equivalents to private
or public
encapsulation in 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.
In ECMAScript, you can override functions on an instance of a class, as it is just an object like everything else.
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
Using .constructor.prototype
of an object gets the prototype of the object when it has been instantiated with the new
keyword.
Inheritance is only of structure and behaviour - only structure and behaviour is inherited?
It is a cumbersome wording they have chosen. What I believe they mean is that as in ECMAScript, an instance of a class is just another object like anything else, you can modify almost everything about it, while in C++, an instance of a class has stricter requirements. You can't add new methods to an instance, you can't add new properties, and you can't break the constraints that the language has provided for you.
I hope this answers your questions. If anything isn't clear, please comment and I'll edit it.
回答2:
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 systems are one where we keep the programs and the data together - so that the way of handling a piece of data is kept together with the data itself, encapsulated together in an object.
There are generally two types of object-oriented languages. Class-based languages and prototyped languages. In a Class-based languages, the behaviour of the objects, and the definition of the data they will hold, is defined in a Class or a Class definition. The Classes do very little work other than that. Objects are created as little worker bee copies of the Class, and they do as the Class has defined them as doing. These instance objects do the vast bulk of the actual work.
In prototyped languages, there is no separate thing holding the definition of what the object does. The object in question holds it's own definitions.
State is just a fancy way of saying the data. More precisely, it's the value of the attributes of the object at an exact given moment in time.
Methods is just a contraction of "the object's method of responding to a message".
If the original developers of object-orientation had talked about "the object's way of responding to a message" we'd be talking about "ways" nowadays. (If they'd said they created particular categories of things, then we'd now have Thing-Oriented Development, and we'd have Category-based thing-oriented languages and prototype-based thing-oriented languages. The point is, don't get hung up about the language. But I digress).
So... let's have some examples. Modern Object-orientation comes from Xerox Corporation's Palo Alto Research Center (known as PARC). So I'll use examples from there.
Smalltalk is the King Daddy of object-oriented languages. (Simula had done some pioneering in this field, and was a strong influence on the early versions of Smalltalk, even up to Smalltalk-72. But Smalltalk and object-orientation, as we know it today, really arrives with Smalltalk-80).
Smalltalk is a Class-based language.
You define a Class. Say, the Class 'Person'. We need to store some data. Let's go with "Given name", "Family name", "Date of Birth", and "Full name". These definitions are stored in the Class.
We'll also define a message protocol - the list of messages that an object of Class Person knows how to respond to. Then we define a method of responding to each message.
These definitions get stored in the Class. When aPerson
(i.e an instance of Class
Person
, which is the same as an object belonging to Class Person, with the name aPerson) needs to respond to the message, it looks up the method of responding in the Class definitions. (The class is itself an object instance of Class Metaclass, but we'll stay out of that rabbit-hole in this answer).
So, you write code. You store it in Class Person.
You define variables. You store the definition of the data structure for each variable in the Class Person.
You create an object that belongs to the Person Class. We'll call it aPerson. The data will get stored in aPerson. The format and structure of the data is defined in the Person Class.
Let's define some methods.
First, we write a method that will create anArbitraryPerson object
This method is defined in the Class. It will be executed by the Class itself. So it is called a class method.
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."
So our first method is a Class method, that creates a new Person object. This new object belongs to that specific Class of objects.
We obviously need a message that can set the first name of an object of Class Person.
We define this method in Class Person. But it will only be used by instances of Class Person.
Person >> named: aFirstNameString
"Set the givenName variable of this object to aString.
Return the object"
^self givenName: aFirstNameString
self
is the object that has received the message. The object looks up this piece of code which is stored as part of Class Person's definitions when it needs to respond to the message known as named
.
aFirstNameString
is just a placeholder for the parameter or argument that we pass into the method.
We define standard getters and setters (aka accessors) for each variable that has its data stored in an object.
Person >> givenName: aString
"Set the givenName variable of this object to aString"
givenName := aString
.
Person >> givenName
"return the givenName of the receiver"
^givenName
We can do the same for other instance variables : familyName
, dateOfBirth
, fullName
.
fullName
is a derived attribute of Person
. We'll deal with it a little differently, by also giving it an additional method
Because it is derived, we'll need an additional message to derive it.
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
So let's use the methods.
anArbitraryPerson := Person named: 'Tom'.
anotherArbitrary := Person named: 'Dick'.
aThirdArbitraryPerson := Person named: 'Harry'.
(this reads as: assign the result of calling the method "Person named: 'Tom'
" to anArbitraryPerson )
So, if we execute those three statements we end up creating three instance of the Person Class.
The state of their instance variables - i.e. the values that their attributes have been set to - is stored in the object. The method code is stored in the Class. But every object has precisely and only one Class, so that's not a problem. We always know exactly which Class to look-up the method definition in.
If we were to send these messages:
anArbitraryPerson familyName 'Jones'.
anotherArbitrary familyName 'Emery'.
aThirdArbitraryPerson := Person named: 'Windsor'.
we would have changed the state of each and every one of those objects.
We can look inside the objects and see the data. We have to look intside the Class to see the method definitions. We have to look inside the Class to see the data structure definitions.
You can find more resources at Beginning to Smalltalk (link) and many other fine Smalltalk websites.
So now for prototype-based object-oriented languages. I'll use another OO-language from Xerox Parc - LambdaMoo code.
This was created for permanently-on object database servers. Think erlang-like uptimes. Smalltalk does the same thing - running and running, essentially forever, and accepting all changes to the system while it is running.
@create $thing named "person"
@create person named "Tom"
@create person named "Dick"
@create person named "Harry"
We don't refer to any class. We just create the objects directly.
In Lambda, objects have "verbs" and "properties". And the verb definitions and property definitions are stored in the object itself. And the state of the properties - the particular values of the data that the object encapsulates - is stored in the object too.
If we want, we can change all the behaviour on each one (we can override it) - because they are all just objects. And they store their own method code. If the obhject doesn't have a message in its own protocol, it'll ask it's parents and grandparents and great-grandparents, until either of them has a method of dealing with the message, or you run out of ancestors to ask.
For a fuller beginners tutorial to OO more details Yib's Pet Rock - A Programming Primer for Beginners. (link) Part of Yib's Guide to Mooing.
A better approach might be:
@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"
Which allows us to send the message to the object, and so invoke the method on the object, and so change the state of the object.
Tom.setFullName
Now we have the methods carried by the objects, along with the attribute definitions. And the state of the data.
In Smalltalk, the classes carried the methods (i.e. the method definitions ) and the attribute definitions. The objects themselves only carried the state of the data.
来源:https://stackoverflow.com/questions/34010495/class-based-and-object-based-languages-comparison-ecmascript-specification