Javascript Object-Oriented Programming (lower)-inheritance and Polymorphism

Source: Internet
Author: User
Tags variable scope

We have discussed how to encapsulate private instance members, public instance members, Private Static members, public static members, and static classes in JavaScript. This time we will discuss the other two elements of object-oriented programming: inheritance and polymorphism.

1. Several Basic Concepts
Why?

When discussing inheritance, we have already listed some basic concepts that are closely related to encapsulation. Today we will discuss the basic concepts, it is mainly related to inheritance and polymorphism, but they are also related to encapsulation.

1.1 Definition and assignment
Variable definition refers to the use

VaR;
Declare variables in this form.

Function definition refers to the use

Function (...){...}
Declare a function in this form.

VaR A = 1;
Is two processes. The first process is to define variable A, and the second process is to assign values to variable.

Similarly

VaR A = function (...){};
The first process is to define variable A and an anonymous function. The second process is to assign an anonymous function to variable.

Variable definition and function definition are completed before the execution of the entire script, while variable assignment is completed in the execution phase.

The function of a variable definition is to specify its scope for the declared variable. The variable definition does not give the initial value of the variable, any variables that are not defined and directly used, or variables that are defined but not assigned values, their values are all undefined.

In addition to declaring the scope of a function, the function definition also defines the function body structure. This process is recursive. That is to say, the definition of the function body includes the definition of variables and functions in the function body.

Through the following example, we can better understand this point:

Alert ();
Alert (B );
Alert (C );
VaR A = "";
Function (){}
Function B (){}
VaR B = "B ";
VaR c = "C ";
VaR c = function (){}
Alert ();
Alert (B );
Alert (C );
Guess what the execution result of this program is? Run the following command to see if it is the same as what you think. If it is the same as what you think, it means that you have understood the above.

The results of this program are very interesting. Although the first alert (a) is at the beginning, you will find that the output value is actually function, the function definition is indeed completed before the entire program is executed.

Let's look at B. function B is defined before variable B, but the first alert (B) still outputs function B, A variable definition does not do anything about a variable. It only declares its scope and does not overwrite the function definition.

Finally, in C, the first alert (c) output is undefined, which means var c = function () {} is not defined for Function C, just define a variable C and an anonymous function.

Let's look at the second Alert (a). You will find that the output is actually a, which means that the value assignment statement is indeed completed during execution. Therefore, it overwrites the definition of function.

The second Alert (B) is of course the same. The output is B, which means that no matter whether the value assignment statement is written before the function definition or after the function definition, A value assignment to a variable with the same name as a function always overwrites the function definition.

The second Alert (c) Outputs function () {}. This indicates that the value assignment statement is executed sequentially, and the subsequent value assignment overwrites the previous value assignment, whether the value is a function or another object.

After understanding the content mentioned above, I think you should know when to use function x (..){...}, When should I use VaR x = function (...) {...} Right?

Finally, let us also remind you that if the variable definition and Function Definition appear in Eval, they are completed in the execution phase. Therefore, do not use eval! In addition, even if you want to use Eval, do not use local variables and local methods in it!

1.2 This and execution Context
We have already touched this before we discussed encapsulation. In the discussion of encapsulation, we can see that this represents the instantiated object of the class where this is located. Is that true?

Let's take a look at the following example in the pipeline:

VaR x = "I'm a global variable! ";
Function Method (){
Alert (X );
Alert (this. X );
}
Function class1 (){
// Private Field
VaR x = "I'm a private variable! ";
// Private Method
Function Method1 (){
Alert (X );
Alert (this. X );
}
VaR method2 = method;
// Public field
This. x = "I'm a object variable! ";
// Public Method
This. Method1 = function (){
Alert (X );
Alert (this. X );
}
This. method2 = method;
// Constructor
{
This. Method1 (); // I'm a private variable!
// I'm a object variable!
This. method2 (); // I'm a global variable!
// I'm a object variable!
Method1 (); // I'm a private variable!
// I'm a global variable!
Method2 (); // I'm a global variable!
// I'm a global variable!
Method1.call (this); // I'm a private variable!
// I'm a object variable!
Method2.call (this); // I'm a global variable!
// I'm a object variable!
}
}
 
VaR o = new class1 ();
Method (); // I'm a global variable!
// I'm a global variable!
O. Method1 (); // I'm a private variable!
// I'm a object variable!
O. method2 (); // I'm a global variable!
// I'm a object variable!

Why is this?

Let's take a look at the execution context. What is the execution context?

If a method is being executed, the execution context is the object attached to the method, the created object is the execution context.

If a method does not explicitly attach to an object during execution, its execution context is a global object (top-level object), but not necessarily a global object. Global objects are determined by the current environment. In the browser environment, the Global object is the window object.

Global variables and global functions that are defined outside all functions are attached to global objects. Local variables and local functions defined in the function are not attached to any objects.

Is the execution context related to the variable scope?

The execution context is different from the variable scope.

When a function is assigned to another variable, the scope of the variable used inside the function will not change, however, its execution context changes to the object attached to this variable (if this variable has an object attached to it ).

The call and apply methods on the function prototype can change the execution context, but do not change the variable scope.

To understand the above words, you only need to remember one thing:

The scope of a variable is determined at the time of definition and will never change. The execution context is determined at the time of execution and can be changed at any time.

In this way, we can easily understand the example above. This. when executing the statement Method1 () (note that the function body has not been entered here), the object is being created, and the current execution context is the object being created, therefore, this points to an object that is currently being created. method1 () when this method is executed (this refers to the function body), the object attached to the method being executed is also the object being created, so it contains this. this of X is also the same object, so the output you see is I'm a object variable! .

While executing the Method1 () function (after entering the function body), Method1 () does not explicitly attach to an object, although it is defined in class1, however, it is not attached to class1, nor to objects after class1 instantiation, but its scope is limited to class1. Therefore, its ancillary object is actually a global object, so when it is executed to alert (this. x), this. X is the value defined in the global environment as "I'm a global variable!" .

Although method2 () is defined in class1, method () is defined outside class1. When method is assigned to method2, the scope of method is not changed, when executing method2, it is still executed in the scope defined by method. Therefore, what you see is two I'm a global variable! Output. Similarly, when this. method2 () is called, alert (x) Outputs I'm a global variable! This is also the reason.

Because call changes the execution context, this. x changes to I'm a object variable when method1.call (this) and method2.call (this) are used !. However, it cannot change the scope, so the result of X is the same as that of calling without using the call method.

And we will execute O. in Method1 (), alert (x) does not use this to indicate the execution context of X. Then, X indicates the recently defined variable in the scope of the currently executed function. Therefore, the output is I'm a private variable !. Final output I'm a object variable! I don't want to say that everyone knows why? : D

2 Inheritance and Polymorphism
2.1 starting from Encapsulation
As we mentioned earlier, the purpose of encapsulation is to hide data.

At a deeper level, encapsulation in Javascript has the following benefits:

1. Stealth implementation details. when the implementation of the private part is completely rewritten, the caller's behavior does not need to be changed. This is also the main purpose of encapsulation for other object-oriented languages.

2. In JavaScript, local variables and local functions are faster to access. Therefore, private fields are encapsulated with local variables. Encapsulating private methods with local methods can improve script execution efficiency.

3. For JavaScript compression obfuscators (as far as I know, the best JavaScript analysis, compression, and obfuscators are JSA), both local variables and local function names can be replaced, however, the global variables and global function names cannot be replaced (in fact, this is also true for Javascript script parsers ). Therefore, for open-source or non-open-source javascript programs, when private fields and private methods use encapsulation technology, you can define long enough ideographic names for them when writing code, code readability is increased, and they can be replaced with some very short names (generally single character names) during release, so that they can be fully compressed and obfuscated. And reduces bandwidth usage, and can truly hide details.

Therefore, encapsulation is very useful for JavaScript!

So what is the purpose of JavaScript inheritance?

2.2 why inherit
In other object-oriented programming languages, apart from reducing repeated code writing, inheritance is most useful for polymorphism. This is especially true for strong-type languages:

1. In a strongly typed language, a variable cannot be assigned two values of different types, unless these two types are compatible with the type of the variable, this compatible relationship is implemented by inheritance.

2. In a strongly typed language, methods cannot be directly expanded or rewritten for an existing type. To expand a type, the only method is to inherit it, in its subclass, it is expanded and rewritten.

Therefore, for strong object-oriented languages, the implementation of polymorphism depends on the implementation of inheritance.

For the Javascript language, inheritance is less important for implementing polymorphism:

1. In JavaScript, a variable can be assigned a value of any type and can call methods of the same name on any type of object in the same way.

2. In JavaScript, existing types can be expanded and rewritten directly through prototype.

Therefore, in Javascript, inheritance is mainly used to reduce the compilation of repeated code.

The two inheritance methods we will discuss next may be familiar to everyone. One is the prototype Inheritance Method and the other is the inheritance method, which will not produce any side effects. We mainly discuss the nature of the two methods and what needs attention.

2.3 prototype Inheritance Law
In JavaScript, each class (function) has a prototype. Members of this prototype are passed to the instantiated object of this class when the class is instantiated. There is no prototype on the instantiated object, but it can be used as the prototype of another class (function). When the class is instantiated with this object as the prototype, the members on the object will be passed to the instantiated object of the class as the prototype. This is the essence of prototype inheritance.

Prototype inheritance is also an inheritance method used by many native objects in JavaScript.

Function parentclass (){
// Private Field
VaR x = "I'm a parentclass field! ";
// Private Method
Function Method1 (){
Alert (X );
Alert ("I'm a parentclass method! ");
}
// Public field
This. x = "I'm a parentclass object field! ";
// Public Method
This. Method1 = function (){
Alert (X );
Alert (this. X );
Method1 ();
}
}
Parentclass. Prototype. method = function (){
Alert ("I'm a parentclass prototype method! ");
}
Parentclass. staticmethod = function (){
Alert ("I'm a parentclass static method! ");
}
 
Function subclass (){
// Private Field
VaR x = "I'm a subclass field! ";
// Private Method
Function method2 (){
Alert (X );
Alert ("I'm a subclass method! ");
}
// Public field
This. x = "I'm a subclass object field! ";
// Public Method
This. method2 = function (){
Alert (X );
Alert (this. X );
Method2 ();
}
This. method3 = function (){
Method1 ();
}
}
 
// Inherit
Subclass. Prototype = new parentclass ();
Subclass. Prototype. constructor = subclass;
 
// Test
VaR o = new subclass ();
 
Alert (O instanceof parentclass); // true
Alert (O instanceof subclass); // true
 
Alert (O. constructor); // function subclass (){...}
 
O. Method1 (); // I'm a parentclass field!
// I'm a subclass object field!
// I'm a parentclass field!
// I'm a parentclass method!
O. method2 (); // I'm a subclass field!
// I'm a subclass object field!
// I'm a subclass field!
// I'm a subclass method!
O. Method (); // I'm a parentclass prototype method!
 
O. method3 (); // Error !!!
Subclass. staticmethod (); // Error !!!

The above example shows how to use the prototype inheritance method to implement inheritance.

There are two key steps to inherit from prototype:

First, create an instantiated object of the parent class, and then assign the object to the prototype attribute of the subclass.

In this way, all public instance members in the parent class will inherit from the quilt class. When the instanceof operator is used to determine whether the instantiated object of the subclass belongs to both the subclass and the parent class.

Then assign the subclass itself to its prototype constructor attribute. (Note: when assigning values here, there is no !)

This step ensures that the subclass definition is displayed when you view the constructor attribute of the instantiated object of the subclass, rather than the definition of its parent class.

Next. in the result of the Method1 () call, we can see that in the public instance method inherited by the subclass, if the private instance field or private instance method is called, the called Private instance members belong to the parent class.

Similarly. the results of the method2 () call show that if the private instance field or private instance method is called, The called Private instance members belong to the subclass.

Through the result of calling O. Method (), we can see that the method defined on the parent class prototype will inherit the quilt class.

The result of calling O. method3 () shows that the instance method defined in the subclass cannot access the private instance members defined in the parent class.

Finally, we can see from the results of the call to subclass. staticmethod () that static members will not be inherited.

2.4 call the Inheritance Law
The essence of calling inheritance is that in the constructor of the subclass, the constructor method of the parent class is executed in the context of the sub-class execution, in the parent constructor method, all the content operated by this method is actually the content on the instantiated object of the subclass operated. Therefore, this approach is only intended to reduce repeated code writing.

Function parentclass (){
// Private Field
VaR x = "I'm a parentclass field! ";
// Private Method
Function Method1 (){
Alert (X );
Alert ("I'm a parentclass method! ");
}
// Public field
This. x = "I'm a parentclass object field! ";
// Public Method
This. Method1 = function (){
Alert (X );
Alert (this. X );
Method1 ();
}
}
Parentclass. Prototype. method = function (){
Alert ("I'm a parentclass prototype method! ");
}
 
Parentclass. staticmethod = function (){
Alert ("I'm a parentclass static method! ");
}
 
Function subclass (){
// Inherit
Parentclass. Call (this );
 
// Private Field
VaR x = "I'm a subclass field! ";
// Private Method
Function method2 (){
Alert (X );
Alert ("I'm a subclass method! ");
}
// Public field
This. x = "I'm a subclass object field! ";
// Public Method
This. method2 = function (){
Alert (X );
Alert (this. X );
Method2 ();
}
This. method3 = function (){
Method1 ();
}
}
 
// Test
VaR o = new subclass ();
 
Alert (O instanceof parentclass); // false
Alert (O instanceof subclass); // true
 
Alert (O. constructor); // function subclass (){...}
 
O. Method1 (); // I'm a parentclass field!
// I'm a subclass object field!
// I'm a parentclass field!
// I'm a parentclass method!
O. method2 (); // I'm a subclass field!
// I'm a subclass object field!
// I'm a subclass field!
// I'm a subclass method!
 
O. Method (); // Error !!!
O. method3 (); // Error !!!
Subclass. staticmethod (); // Error !!!

The above example shows how to use the inheritance method to implement inheritance.

The key to inheritance by calling is only one step:

When defining a subclass, The this pointer of the subclass is passed in through the call method of the parent class. Make the parent class method run in the context of the subclass.

In this way, all the public instance members defined in this mode within the parent class will inherit from the quilt class.

When determined by the instanceof operator, the instantiated object of the subclass only belongs to the subclass and does not belong to the parent class.

When viewing the constructor attribute of the instantiated object of the subclass, we can see the definition of the subclass, not the definition of its parent class.

Next, the results of calling O. Method1 () and O. method2 () are the same as those of the prototype inheritance method, and the problems described are the same.

Through the result of calling O. Method (), we can see that the method defined on the parent class prototype will not inherit from the quilt class.

By calling O. method3 (), we can see that the instance methods defined in the subclass cannot access the private instance members defined in the parent class.

Finally, we can see from the results of the call to subclass. staticmethod () that static members will not be inherited.

Finally, this example does not show that multiple inheritance can be implemented by calling the inheritance law. That is to say, a subclass can inherit from multiple parent classes all the public instance members defined in the parent class by using this method.

As a weak language, JavaScript provides abundant polymorphism, Which is incomparable to other strong object-oriented languages.

Polymorphism
Heavy Load and coverage
The differences between heavy load and overwrite are described first. Overload is used in English and override is used in English. It is found that most people on the internet use override as a heavy load, which is not correct. There is a difference between heavy load and coverage.

Overload means that a function with the same name (note that this includes a function) or method can have multiple implementations, which are identified by the parameter type and the number of (or) parameters.

Override means that sub-classes can define methods with the same name as the parent class and the same parameter type and number. After the definition of these methods, in the instantiated object of the sub-classes, the methods inherited from the parent class with the same name will be hidden.

Heavy Load
In JavaScript, function parameters are of no type and the number of parameters is arbitrary. For example, although you can define:

Function add (a, B ){
Return A + B;
}

Such a function, but you can still call it to include any number of parameters. Of course, the parameter type is also arbitrary. As for whether an error occurs, it is determined by the content executed in this function. JavaScript does not determine which function you call based on the number and type of parameters you specify.

Therefore, to define overload methods, you cannot do the same as in a strongly typed language. However, you can still implement overload. It is through the arguments attribute of the function. For example:

Function add (){
VaR sum = 0;
For (VAR I = 0; I <arguments. length; I ++ ){
Sum + = arguments [I];
}
Return sum;
}

In this way, you can overload any number of parameter addition functions.

Of course, you can also use instanceof or constructor in a function to determine the type of each parameter and decide what operations to perform next to implement more complex functions or method overloading. In short, the Javascript overload is implemented by the user by operating the arguments attribute in the function.

Overwrite
It is easy to implement coverage, for example:

Function parentclass (){
This. method = function (){
Alert ("parentclass method ");
}
}
Function subclass (){
This. method = function (){
Alert ("subclass method ");
}
}
Subclass. Prototype = new parentclass ();
Subclass. Prototype. constructor = subclass;
 
VaR o = new subclass ();
O. Method ();

In this way, the method defined in the subclass overwrites the method inherited from the parent class.

You may say that this overwrite method is good, but in Java, the overwrite method can call the method to be overwritten (the method of the parent class). How can this be implemented here? It is also very easy, and it is more flexible than Java. in Java, you can only use super to call the method that is overwritten. We can not only implement this, but also allow all methods in the subclass to call methods that are overwritten in the parent class. See the following example:

Function parentclass (){
This. method = function (){
Alert ("parentclass method ");
}
}
Function subclass (){
VaR method = This. method;
This. method = function (){
Method. Call (this );
Alert ("subclass method ");
}
}
Subclass. Prototype = new parentclass ();
Subclass. Prototype. constructor = subclass;
 
VaR o = new subclass ();
O. Method ();

You will find that it is so simple that you define a private variable before defining the override method, and then assign the method defined in the parent class to it, then we can continue to call it later, and this method is private and invisible to the subclass object. This is consistent with the coverage of other advanced languages.

Note that when we call this method in the override method, we need to use the call method to change the execution context to this (although not necessary in this example). If we call this method directly, the execution context becomes a global object.

 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.