We discussed how to implement encapsulation of private instance members, public instance members, private static members, public static members, and static classes in a JavaScript language. This time we'll discuss two other elements in object-oriented programming: Inheritance and polymorphism.
1 is also a few basic concepts
Why do you say it again?
In discussing inheritance, we have listed some basic concepts that are closely related to encapsulation, and the basic concepts we are going to discuss today are mainly about inheritance and polymorphism, but they are also associated with encapsulation.
1.1 Defining and assigning values
Variable definition refers to the use of
var A;
This form to declare variables.
function definition refers to the use of
Function A (...) {...}
This form to declare a function.
var a = 1;
is two processes. The first procedure is to define the variable A, and the second procedure is to assign a value to the variable A.
Same
var a = function (...) {};
is also two processes, the first process is to define variable A and an anonymous function, the second process is to assign the anonymous function to the variable A.
Variable definitions and function definitions are completed before the execution of the entire script, and variable assignments are performed in the execution phase.
The function of a variable definition is simply to indicate its scope to the variable being declared, and the variable definition does not give the variable an initial value, any variables that are not defined directly, or a variable that defines but does not assign a value, and their values are undefined.
The function definition also defines the function body structure, in addition to declaring the function outside the function. The process is recursive, that is to say, the definition of a function body includes the definition of variables and functions in the body of a function.
We can understand this more clearly with the following example:
alert (a);
alert (b);
alert (c);
var a = "a";
function A () {}
function B () {}
var b = "B";
var c = "C";
var c = function () {}
alert (a);
alert (b);
Alert (c); Guess what the result of this program is. Then take a look at whether it's the same as you think, and if it's the same as you think, it means you already understand what it says.
The results of this program are interesting, although the first alert (a) is at the front, but you will find that the value it outputs is function A () {}, which means that the function definition is actually complete before the execution of the program.
to see B again, function B is defined before variable B, but the first alert (b) is still function B () {}, which shows that the variable definition does not do anything with the variable, just declare its scope, and it does not overwrite the function definition.
The last look at C, the first alert (c) output is undefined, which indicates that var c = function () {} is not defined on function C, just defines a variable c and an anonymous function.
to look at the second alert (a), you'll see that the output is actually a, which means that the assignment statement is actually done during execution, so it overrides the definition of function A.
The second alert (b), of course, is the same, and the output is B, which means that assigning a variable with the same name as a function always overrides the function definition, regardless of whether the assignment statement was written before the function definition or after the function definition.
The second alert (c) outputs a function () {}, which indicates that the assignment statement is executed sequentially and that the subsequent assignment overrides the previous assignment, regardless of whether the assigned value is a function or another object. The
understands the above and I think you should know when to use function x (..) {...}, when should I use var x = function (...) {...} it.
Finally, remember that if you have variable definitions and function definitions in eval, they are done in the execution phase. So, do not use eval unless you have to. Also, do not use local variables and local methods in the inside, even if you want to use Eval.
1.2 This and execution contexts
We have contacted this when we discussed the encapsulation earlier. In the discussion of encapsulation, we see this as the instantiation object itself that represents the class in which this is located. Is that really true?
Let's take a look at the following example:
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 this is the result.
So let's take a look at what is the execution context. What is the execution context?
If you are currently executing a method, the execution context is the object that the method is attached to, and if you are currently executing a procedure that creates an object (that is, created by new), the object created is the execution context.
If a method is not explicitly attached to an object at execution time, its execution context is a global object (the top-level object), but it is not necessarily subordinate to the global object. Global objects are determined by the current environment. In a browser environment, the global object is the Window object.
Global variables and global functions defined outside all functions are subordinate to global objects, and local variables and local functions defined within the function are not attached to any object.
The execution context has nothing to do with the scope of the variable.
The execution context is different from the scope of the variable.
When a function is assigned to another variable, the scope of the variable used inside the function does not change, but its execution context becomes the object that the variable is attached to (if the variable has a subordinate object).
The call and apply method on the function prototype can change the execution context, but it does not change the scope of the variable.
To understand these words, you only need to remember one point:
The scope of a variable is determined at definition, it will never change, and the execution context is determined at execution time, and it can be changed at any time.
So it's not hard to understand the example above. THIS.METHOD1 () This statement (note that this is not yet entered into the function body) is being created, the current execution context is the object being created, so this is also the object being created, in This.method1 () When this method executes (this is the entry into the function body), the object being attached to the executing method is also the object being created, so this is the same object as the this.x in it, so the output you see is the I ' m a object variable! Up.
While executing the METHOD1 () (refers to the function body), method1 () is not explicitly attached to an object, although it is defined in Class1, but he is not attached to the Class1, nor is it attached to the Class1 instantiated object, But its scope is limited to the Class1. Therefore, its subordinate object is actually a global object, so when it is executed to alert (this.x), this.x becomes the X of the value "I ' m a global variable!" defined in the global environment.
METHOD2 () is defined in Class1, but method () is defined outside of Class1, and method is assigned to METHOD2 without changing the scope of method, so when METHOD2 executes, it is still in the Metho D is executed within the scope defined, so what you see is two I ' m a global variable! The output. Similarly, when THIS.METHOD2 () is invoked, alert (x) outputs I ' m a global variable! And that's the reason.
Because call changes the execution context, the this.x becomes the I ' m a object variable! through Method1.call (this) and Method2.call (this). But it does not change the scope, so X is still the same as the result of not using the call method.
And when we execute O.METHOD1 (), when alert (x) does not use this to indicate the execution context of x, then x represents the most recently defined variable in the scope of the currently executing function, so the output is I ' m a private variable!. The last output I ' m a object variable! I don't think I need to say that everyone knows why. :D
2 Inheritance and polymorphism
2.1 Starting from encapsulation
As we said earlier, the purpose of encapsulation is to implement data hiding.
But on a deeper level, there are several benefits to encapsulation in javascript:
1, stealth implementation details, when the implementation of the private part is completely rewritten, do not need to change the behavior of the caller. This is also the main purpose of other object-oriented languages to implement encapsulation.
2, JavaScript, local variables and local functions to access faster, so the private field with local variables to encapsulate, the private approach to the local method of encapsulation can improve the execution efficiency of the script.
3, for JavaScript compression obfuscation (as far as I know, the best JavaScript analysis, compression, obfuscation is JSA), local variables and local function names can be replaced, and global variables and global function names can not be replaced (in fact, for This is also true of the JavaScript script parser when it works. Therefore, whether for open source or non-open-source JavaScript programs, when the proprietary and private methods use encapsulation technology, the code can be written with long enough ideographic names to increase the readability of the Code, and when published, they can be replaced with some very short names (typically single character names). This will allow for full compression and confusion. and reduce the bandwidth consumption, but also to achieve the real details of the hidden.
So, encapsulation is very useful for JavaScript.
So what is the purpose of implementing inheritance in JavaScript?
2.2 Why to inherit
In other object-oriented programming languages, inheritance, in addition to reducing the number of duplicated code, is most useful to achieve polymorphism. This is especially true in strongly typed languages:
1. In strongly typed languages, a variable cannot be given two values of different types, unless the two types are compatible with the type of the variable, and the compatible relationship is implemented by inheritance.
2. In a strongly typed language, an existing type cannot be expanded and rewritten directly, and the only way to extend a type is to inherit it and expand and rewrite it in its subclasses.
Therefore, for strongly typed object-oriented languages, the implementation of polymorphism relies on the implementation of inheritance.
For JavaScript languages, inheritance is less important for implementing polymorphism:
1. In the JavaScript language, a variable can be given any type of value, and you can invoke the same method on objects of any type in the same way.
2. In JavaScript language, the existing types can be directly expanded and rewritten by means of prototypes.
So, in JavaScript, the main function of inheritance is to reduce the coding of repetitive code.
The next two approaches to implementing inheritance may be familiar, one is the prototype inheritance method, the other is invoking the inheritance method, neither of which has a side effect. Our main discussion is the nature of the two methods and the places to be noted.
2.3 Prototype Inheritance Method
In JavaScript, each class (function) has a prototype that is passed to the class's instantiated object when the class is instantiated. There is no prototype on the instantiated object, but it can be a prototype of another class (function), and when instantiated by a class that is a prototype of that object, the member on that object is passed on to the instantiated object of the class that it is a prototype. This is the essence of archetypal inheritance.
Prototype inheritance is also the 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.staticmet Hod = 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 is a good reflection of how to use the prototype inheritance method to implement inheritance.
The key to
Using prototype inheritance is two-step:
to first create an instantiated object of a parent class, and then assign the object to the prototype property of the subclass.
This way, all public instance members in the parent class inherit from the quilt class. And when judged with the instanceof operator, the instantiated object of the subclass belongs to both the subclass and the parent class. The
then assigns the child class itself to its prototype constructor property. (Note: There is no () when the value is assigned.
This step is to ensure that when you view the constructor property of the instantiated object of a subclass, you see the definition of the subclass, not the definition of its parent class.
Next, by the result of the call to O.method1 (), we see that in a public instance method that is inherited by a subclass, if a private instance field or private instance method is invoked, the private instance members that are invoked are of the parent class.
Similarly, by the result of the O.METHOD2 () call, we see that the instance method defined in the subclass, if a private instance field or private instance method is invoked, is a subclass of these private instance members that are called.
By the result of the O.method () call, we see that the method defined on the parent prototype will inherit from the quilt class.
By the result of the call to O.METHOD3 () we see that the instance method defined in the subclass is not accessible to private instance members defined in the parent class.
Finally, we see that static members are not inherited by the results of the Subclass.staticmethod () call. The essence of the
2.4 call inheritance Method
Invocation inheritance is that in the constructor of a subclass, let the constructor method of the parent class execute on the execution context of the subclass, and all content that is manipulated by this mode on the parent constructor method is actually the content on the instantiated object of the operation's subclass. Therefore, this practice is only to reduce the duplication of 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.staticmet Hod = 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 is a good reflection of how to use the Invoke inheritance method to implement inheritance.
There is only one step to using the key to inherit from the call:
is to pass the this pointer of the subclass to the child class when it is defined, through the parent class's calling method. Causes the parent class method to execute in the subclass context.
In this way, all public instance members within the parent class that are defined through this method are inherited by the quilt class.
when judged with the instanceof operator, the instantiated object of a subclass belongs only to subclasses and not to the parent class.
When you view the constructor property of a subclass's instantiated object, you see the definition of the subclass, not the definition of its parent class.
Next, the result of the call to the O.METHOD1 () and O.METHOD2 () is the same as the result of the invocation of the prototype inheritance method, and the problem is the same, not repeated here.
By the result of the O.method () call, we see that the method defined on the parent prototype will not inherit from the quilt class.
The result of the call to O.method3 () shows that the instance method defined in the subclass also cannot access the private instance members defined in the parent class.
Finally, we see that static members are also not inherited by the results of the Subclass.staticmethod () call.
Finally, there is a point that is not reflected in this example, that is, by invoking the inheritance method, multiple inheritance can be implemented. That is, a subclass can inherit all public instance members that are defined within the parent class from multiple parent classes.
as a weakly typed language, JavaScript provides rich polymorphism, and JavaScript polymorphism is not comparable to other strongly typed object-oriented languages.
Polymorphic
Overloads and overrides
first illustrate the difference between overloading and overriding. Overloaded English is overload, covering English is override. It is wrong to find that most people on the Internet regard override as an overload. There is a difference between overloading and overwriting.
overloading means that a function of the same name (note that the function is included here) or a method can have multiple implementations, and they rely on the number of types and/or parameters of the parameter to distinguish between them. The
overrides mean that subclasses can define methods that have the same name as the parent class, and that the same type and number of parameters are defined, and that the methods that inherit from the parent class in the instantiated object of the subclass are hidden. The parameters of a function in
overloaded
JavaScript are not of type, and the number of arguments is arbitrary, for example, although you can define one:
function Add (A, b) {return
a + b;
}
Such a function, but you can still call it to bring in any number of parameters, of course, the parameter type is arbitrary. As for whether or not to go wrong, that's what the function does, and JavaScript doesn't judge which function you're calling based on the number of parameters and the type of parameter you specify.
Therefore, to define an overloaded method, you cannot do so as in a strongly typed language. But you can still implement overloads. Is through the arguments property of the function. For example:
function Add () {
var sum = 0;
for (var i = 0; i < arguments.length i++) {
sum + + arguments[i];
}
return sum;
}
This allows you to implement an overload of 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, to decide what to do later, to implement more complex functions or method overloads. In short, the JavaScript overload is implemented in a function by the user's own manipulation arguments this property.
Covered
It is also easy to implement overrides, 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 ();
This way, the method defined in the subclass overrides the methods that are inherited from the parent class.
You might say that this kind of overlay is nice, but in Java, the overridden method can call the overridden method (the parent's method), and how to do it here. Also easy, and more flexible in Java than in Java, you can only use super to invoke the secondary overridden method in a method that overrides the overridden method. Not only can we do this, but we can also make it possible for all the methods in the subclass to call the overridden methods in the parent class. Look at 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 was so simple, as long as you define a private variable before you define the override method, assign it to the method that will be overridden in the parent class, and then we can continue to call it later, and this is a private method that is not visible to the object of the subclass. This is consistent with the implementation of other high-level languages.
Finally, it is important to note that when we invoke this method in the overwrite method, we need to use the call method to change the execution context to this (although not necessary in this case), if the method is called directly, the execution context becomes the global object.