The failure of objects can be determined by their layers and classes, but by the current features.However, we should also consider another model.
Based on Dynamic classWe believe that the difference "class VS prototype" shown in the above example is not so important in this dynamic class-based model (especially if the prototype chain is unchanged, for more accurate differentiation, it is still necessary to consider a static class ). For example, it can also use Python or Ruby (or other similar languages ). These languages use the dynamic-class paradigm. However, in some aspects, we can see some features implemented based on the prototype.
In the following example, we can see that only the prototype is based on the delegate. We can enlarge a class (prototype) to influence all objects related to this class, we can also dynamically change the class of this object at runtime (provide a new object for the delegate) and so on.
# Python class A (object): def _ init _ (self, a): self. a = a def square (self): return self. a * self. a = A (10) # create an instance print (. a) #10. B = 20 # provide a new attribute print (. b) #20-access. B = 30 # create a's own attribute print (. b) #30 del. B # Delete attributes print (. b) #20-obtain (prototype) from the class again # It is like a prototype-based model # The prototype class B (object) of the object can be changed at runtime ): # null Class B pass B = B () # B's instance B. _ class _ = A # dynamically changing A class (prototype) B. a = 10 # create a new property print (B. square () #100-A class method available at this time # It can display the reference del Adel B on the delete class # But the object still has an implicit reference, and these methods are still available print (B. square () #100 # But the class cannot be changed at this time # This is the implementation Feature B. _ class _ = dict # error
The implementation in Ruby is similar: Completely dynamic classes are also used (by the way, in the current version of Python, the comparison with Ruby and ECMAScript is to enlarge the class (prototype) (NO), we can completely change the features of objects (or classes) (adding methods/attributes to classes, and these changes will affect existing objects). However, it cannot dynamically change the class of an object.
However, this article is not specific to Python and Ruby, so we will continue to discuss ECMAScript itself.
But before that, let's take a look at some "syntactic sugar" in OOP, because many previous articles on JavaScript often address these issues.
The only wrong sentence to be noted in this section is: "JavaScript is not a class. It has an original type and can replace the class ". It is very important to know that not all class-based implementations are completely different, even if we may say "JavaScript is different", it is also necessary to consider (except the concept of "class) there are other related features.
Other features of various OOP implementationsThis section briefly introduces other features and code reuse methods in various OOP implementations, including OOP implementation in ECMAScript. The reason is that the previously mentioned implementation of OOP in JavaScript has some habitual thinking limitations. The only major requirement is that it should be proved technically and ideologically. It cannot be said that JavaScript is not a pure OOP language without discovering the syntax sugar function in other OOP implementations.
PolymorphismObjects in ECMAScript have several meanings of polymorphism.
For example, a function can be applied to different objects, just like the features of the native object (because the value is determined when the execution context is entered ):
function test() { alert([this.a, this.b]);} test.call({a: 10, b: 20}); // 10, 20test.call({a: 100, b: 200}); // 100, 200 var a = 1;var b = 2; test(); // 1, 2
However, there are also exceptions: the Date. prototype. getTime () method always has a Date object according to the standard value. Otherwise, an exception is thrown.
alert(Date.prototype.getTime.call(new Date())); // timealert(Date.prototype.getTime.call(new String(''))); // TypeError
The so-called parameter polymorphism in function definition is equivalent to all data types, but only accepts the polymorphism parameter (such as the. sort sorting method of the array and Its Parameter-the multi-state sorting function ). By the way, the above example can also be considered as a parameter polymorphism.
The method in the prototype can be defined as null. All created objects should be redefined (implemented) This method (I .e., "one interface (signature), multiple implementations ").
Polymorphism is related to the Duck type we mentioned above: that is, the object type and position in the hierarchy are not so important, but if it has the necessary features, it is easy to accept (that is, the general interface is very important, and the Implementation can be varied ).
EncapsulationThere are often incorrect ideas about encapsulation. In this section, we will discuss some syntactic sugar in OOP implementation, which is also a well-known modifier: in this case, we will discuss some convenient "Sugar" implemented by OOP-well-known modifiers: private, protected, and public (or called object access level or access modifier ).
Here, I would like to remind you of the main purpose of encapsulation: encapsulation is an increase in abstraction, rather than selecting a hidden "malicious hacker" that writes something directly to your class ".
This is a big mistake: To hide and hide.
Access Level (private, protected, and public). In order to facilitate programming, it has been implemented in many object-oriented systems (which is really a very convenient syntax sugar) to describe and build systems in a more abstract way.
These can be seen in some implementations (such as Python and Ruby ). On the one hand (in Python), these _ private _ protected attributes (by the naming conventions of underlines) are not accessible from outside. On the other hand, Python can access (_ ClassName _ field_name) from outside through special rules ).
Class A (object): def _ init _ (self): self. public = 10 self. _ private = 20 def get_private (self): return self. _ private # outside: a = A () # print (. public) # OK, 30 print (. get_private () # OK, 20 print (. _ private) # failure, because it can only be used in A # But in Python, you can use special rules to access print (. _ A _ private) # OK, 20
IN Ruby: on the one hand, it has the ability to define private and protected features. On the other hand, it also has special methods (such as instance_variable_get, instance_variable_set, and send) to obtain encapsulated data.
Class A def initialize @ a = 10 end def public_method private_method (20) end private def private_method (B) return @ a + B end a =. new # new instance. public_method # OK, 30. a # failed, @ a-is A private instance variable # private_method is private and can only be accessed in Class. private_method # Error # There is a special metadata method name. You can obtain data. send (: private_method, 20) # OK, 30a. instance_variable_get (: @ a) # OK, 10
The main reason is that the programmer wants to obtain the encapsulated data (note that I do not use "hidden" data in particular. If the data is incorrectly changed or has any errors in some way, it is all the responsibility of the programmer, but it is not a simple "spelling mistake" or "casually changing certain fields ". However, if this happens frequently, it is a bad programming habit and style, because the common value is to use public APIs to "talk" with objects ".
Repeat, the basic purpose of encapsulation is to abstract the data from the user of the auxiliary data, rather than to prevent hackers from hiding the data. More seriously, encapsulation does not require private modification of data to achieve software security.
Encapsulate auxiliary objects (local). We use the minimum cost, localization, and predictive changes to ask for the feasibility of public interface behavior changes. This is also the purpose of encapsulation.
In addition, the setter method ?? The important purpose is to abstract complex computing. For example, the setter element. innerHTML -- abstract statement -- "the HTML in this element is the following content", and the setter function in the innerHTML attribute is difficult to calculate and check. In this case, most of the problems involve abstraction, but encapsulation also occurs.
The concept of encapsulation is not only related to OOP. For example, it can be a simple function that only encapsulates various computations to make them abstract (there is no need to let users know, such as the Math function. round (......) is implemented, the user just calls it ). It is an encapsulation. Note that it is "private, protected, and public ".
In the current version of ECMAScript specification, private, protected, and public modifiers are not defined.
However, in practice, it is possible to see something named "imitating JS encapsulation ". Generally, this context is used (as a rule, constructor itself. Unfortunately, this kind of "Imitation" is often implemented, and programmers can generate pseudo-absolute non-abstract entities to set the "getter/setter method" (I will say it again, it is wrong ):
function A() { var _a; // private a this.getA = function _getA() { return _a; }; this.setA = function _setA(a) { _a = a; }; } var a = new A(); a.setA(10);alert(a._a); // undefined, privatealert(a.getA()); // 10
Therefore, everyone understands that for each created object, the getA/setA method is also created, which is also the cause of memory increase (compared with the prototype definition ). Although, in theory, objects can be optimized in the first case.
In addition, some JavaScript articles often mention the concept of "Private method". Note: The ECMA-262-3 standard does not define any concept about "Private method.
However, in some cases it can be created in the constructor, because JS is an ideological language-the object is completely mutable and has unique features (under certain conditions in the constructor, some objects can obtain additional methods, but others cannot ).
In addition, in JavaScript, if the encapsulation is misinterpreted as an understanding that prevents malicious hackers from automatically writing certain values in place of the setter method, the so-called "hidden) "and" private "are not quite" hidden ". Some implementations can call the context to the eval function (which can be tested on SpiderMonkey1.7) obtain the value on the relevant scope chain (and all corresponding variable objects ).
Eval ('_ a = 100',. getA); // or. setA, because the [[Scope] of the _ a method is. getA (); // 100.
Alternatively, you can directly access the activity object (such as Rhino) in the Implementation. You can change the value of the internal variable by accessing the corresponding properties of the object:
// Rhinovar foo = (function () { var x = 10; // private return function () { print(x); };})();foo(); // 10foo.__parent__.x = 20;foo(); // 20
Sometimes, in JavaScript, the data "private" and "protected" is achieved by prefixing the variable with an underscore (but this is only a naming convention compared with Python ):
var _myPrivateData = 'testString';
It is often used to enclose the execution context in parentheses, but for real auxiliary data, it is not directly associated with the object, but it is convenient to abstract it from external APIs:
(Function () {// initialize context })();
Multi-InheritanceMulti-inheritance is a convenient syntactic sugar for code reuse and improvement (if we can inherit a class at a time, why cannot we inherit 10 classes at a time ?). However, due to the shortcomings of Multi-inheritance, implementation is not popular.
ECMAScript does not support multi-inheritance (that is, there is only one object that can be used as a direct prototype), although its ancestor's self-programming language has such capabilities. However, in some implementations (such as SpiderMonkey), _ noSuchMethod _ can be used to manage scheduling and delegation to replace the prototype chain.
MixinsMixins is a convenient way to reuse code. Mixins has been recommended as a substitute for multi-inheritance. These independent elements can be mixed with any object to expand their functions (So objects can also be mixed with multiple Mixins ). The ECMA-262-3 specification does not define the concept of "Mixins", but according to the Mixins definition and ECMAScript has a dynamic variable object, there is no obstacle to using Mixins to simply expand features.
Typical Example:
// helper for augmentationObject.extend = function (destination, source) { for (property in source) if (source.hasOwnProperty(property)) { destination[property] = source[property]; } return destination;}; var X = {a: 10, b: 20};var Y = {c: 30, d: 40}; Object.extend(X, Y); // mix Y into Xalert([X.a, X.b, X.c, X.d]); 10, 20, 30, 40
Please note that I take these definitions ("mixin", "mix") mentioned in the quotes in the ECMA-262-3, and there is no such concept in the Specification, in addition, it is not a mix but a common method to expand objects through new features. (IN Ruby, the concept of mixins is officially defined. mixin creates a reference containing a module to replace all attributes of this module with simple copying to another module. In fact, the following is true: create an additional object (prototype) for the delegate )).
TraitsTraits and mixins have similar concepts, but they have many functions (according to the definition, because mixins can be applied, they cannot contain States because they may cause naming conflicts ). According to ECMAScript, Traits and mixins follow the same principles. Therefore, this specification does not define the concept of "Traits.
InterfaceThe interfaces implemented in some OOP are similar to mixins and traits. However, compared with mixins and traits, the interface must implement the method signature of the class.
An interface can be considered as an abstract class. However, compared with the abstract class (the methods in the abstract class can only implement one part, and the other part is still defined as a signature), inheritance can only be a single inheritance of the base class, but can inherit multiple interfaces, for this reason, you can use interfaces (multiple mixing) as an alternative to multi-inheritance.
ECMA-262-3 standards neither define the concept of "interface" nor define the concept of "abstract class. However, as an imitation, it can be implemented by an "empty" method (or an exception thrown in an empty method, telling developers that this method needs to be implemented.
Object combinationObject combination is also one of dynamic code reuse technologies. Unlike the inheritance with high flexibility, object combinations implement a dynamic and variable delegate. This is also the basis of the delegated prototype. In addition to the dynamic mutable prototype, this object can be a delegated aggregation object (create a combination as the result -- aggregation), send messages to the object, and delegate to the delegate. This can be more than two delegates, because its dynamic characteristics determine that it can be changed at runtime.
The example of _ noSuchMethod _ already mentioned is as follows, but it also shows how to explicitly use the delegate:
For example:
var _delegate = { foo: function () { alert('_delegate.foo'); }}; var agregate = { delegate: _delegate, foo: function () { return this.delegate.foo.call(this); } }; agregate.foo(); // delegate.foo agregate.delegate = { foo: function () { alert('foo from new delegate'); }}; agregate.foo(); // foo from new delegate
This object relationship is called "has-a", while integration is the "is-a" relationship.
Due to the lack of display combinations (flexibility compared with inheritance), it is also possible to add intermediate code.
AOP featuresFunction decorators can be used as an aspect-oriented function. ECMA-262-3 specifications do not clearly define the concept of "function decorators" (relative to Python, which is officially defined in Python ). However, functions with function-based parameters can be decorated and activated in some aspects (by applying the so-called recommendations ):
Example of the simplest decorator:
function checkDecorator(originalFunction) { return function () { if (fooBar != 'test') { alert('wrong parameter'); return false; } return originalFunction(); };} function test() { alert('test function');} var testWithCheck = checkDecorator(test);var fooBar = false; test(); // 'test function'testWithCheck(); // 'wrong parameter' fooBar = 'test';test(); // 'test function'testWithCheck(); // 'test function'
ConclusionIn this article, we have clarified the introduction to OOP (I hope these materials will be useful to you). In the next chapter, we will continue to implement ECMAScript in object-oriented programming.