1 A few more basic concepts Why do we say this again?
When discussing inheritance, we have already listed some basic concepts, which are closely related to encapsulation. The basic concepts we are going to discuss today are mainly related to inheritance and polymorphism, but they are closely related to encapsulation. There are also some connections.
1.1 Definition and assignment
Variable definition refers to declaring variables in the form of
var a;
.
Function definition refers to declaring a function in the form of
function a(...) {...}
.
var a = 1;
are two processes. The first process is to define the variable a, and the second process is to assign a value to the variable a.
Similarly
var a = function(...) {};
There are also two processes. The first process is to define variable a and an anonymous function, and the second process is to assign the anonymous function to variable a.
Variable definition and function definition are completed before the entire script is executed, while variable assignment is completed during the execution phase.
The function of a variable definition is only to indicate its scope to the declared variable. The variable definition does not give an initial value to the variable. Any variables that are not defined but used directly, or variables that are defined but not assigned a value, their values All are undefined.
In addition to declaring the scope of the 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 within the function body.
We can understand this more clearly through 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 execute it and see if it is the same as you think. If it is the same as you think, it means you have understood what is said above.
The result of this program is very interesting. Although the first alert(a) is at the front, you will find that the value it outputs is function a() {}, which shows that the function definition is indeed executed throughout the program. It's been done before.
Looking at b again, function b is defined before variable b, but the output of the first alert(b) is still function b() {}. This shows that the variable definition does not do anything to the variable, it only declares it. It's just a scope, it doesn't overwrite the function definition.
Finally, look at c. The output of the first alert(c) is undefined, which means that var c = function() {} does not define function c, but only defines a variable c and an anonymous function.
Looking at the second alert(a), you will find that the output is actually a, which shows that the assignment statement is indeed completed during execution, therefore, it covers the definition of function a.
The second alert(b) is of course the same, and the output is b. This means that no matter whether the assignment statement is written before or after the function definition, assigning a value to a variable with the same name as the function will always overwrite the function definition.
The output of the second alert(c) is function() {}, which shows that assignment statements are executed sequentially, and subsequent assignments overwrite previous assignments, regardless of whether the assigned value is a function or other object.
After understanding what is said above, I think you should know when to use function x(..) {…} and when to use var x = function (…) {…}, right?
Finally, I would like to remind you that if variable definitions and function definitions appear in eval, they are completed during the execution phase. So, don’t use eval unless absolutely necessary! In addition, even if you want to use eval, don't use local variables and local methods in it!
1.2 this and execution context
We have already been exposed to this when discussing encapsulation earlier. In the discussion of encapsulation, the this we see represents the instantiated object itself of the class in which this is located. Is this really the case?
Let’s take a look at the following example first:
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 the result?
Let’s first take a look at what execution context is. So what is execution context?
If a method is currently being executed, the execution context is the object attached to the method. If a process of creating an object (created through new) is currently being executed, the created object is the execution context.
If a method is not explicitly attached to an object when executed, its execution context is the global object (top-level object), but it is not necessarily attached 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 attached to the global object, while local variables and local functions defined within the function are not attached to any object.
Is there any relationship between execution context and variable scope?
Execution context and variable scope are different.
When a function is assigned to another variable, the scope of the variable used internally in the function will not change, but its execution context will change to the object to which the variable is attached (if the variable has an attached object) .
The call and apply methods on the Function prototype can change the execution context, but they will not change the variable scope.
To understand the above words, you only need to remember one thing:
The scope of a variable is determined when it is defined, and it will never change; while the execution context is determined when it is executed, and it can be changed at any time. Change.
This way it is not difficult for us to understand the above example. When this.method1() statement (note that what is said here has not yet entered the function body) is executed, the object is being created, and the current execution context is the object being created, so this points to the object currently being created. , when the this.method1() method is executed (here it refers to entering the function body), the object attached to the executing method is also the object being created, so the this in this.x is also the same object. So the output you see is I'm a object variable!.
When executing the function method1() (after entering the function body), method1() is not clearly attached to an object. Although it is defined in class1, it is not attached to class1. , it is not attached to the object instantiated by class1, but its scope is limited to class1. Therefore, its affiliated object is actually a global object. Therefore, when alert(this.x) is executed in it, this.x becomes the value we defined in the global environment as "I'm a global variable!" of x.
Although method2() is defined in class1, method() is defined outside class1. When method is assigned to method2, the scope of method is not changed. Therefore, when method2 is executed, it is still It is executed within the scope where the method is defined, so 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! for this reason.
Because call changes the execution context, this.x becomes I'm a object variable! when passing method1.call(this) and method2.call(this). But it cannot change the scope, so x is still the same as it would have been without the call method.
When we execute o.method1() later, alert(x) does not use this to point out the execution context of x. Then x represents the most recently defined variable in the scope of the currently executed function. Therefore, the output at this time It's I'm a private variable!. The final output is I'm a object variable! I think everyone knows why without me saying it, right?
2 Inheritance and polymorphism
2.1 Start with encapsulation
As we said before, the purpose of encapsulation is to achieve data hiding.
But at a deeper level, encapsulation in javascript also has the following benefits:
1. Invisible implementation details. When the implementation of the private part is completely rewritten, there is no need to change the behavior of the caller. This is also the main purpose of other object-oriented languages to achieve encapsulation.
2. In JavaScript, local variables and local functions are accessed faster, so encapsulating private fields with local variables and encapsulating private methods with local methods can improve the execution efficiency of the script.
3. For javascript compression obfuscator (as far as I know, the best javascript analysis, compression, and obfuscator currently is JSA), local variables and local function names can be replaced, while global variables and Global function names cannot be replaced (in fact, this is also true for the JavaScript parser). Therefore, whether for open source or non-open source JavaScript programs, when private fields and private methods use encapsulation technology, they can be defined with long enough ideographic names when writing code to increase the readability of the code, and when publishing, they can are replaced with some very short names (usually single-character names) so that they can be fully compressed and obfuscated. It also reduces bandwidth usage and can truly hide details.
So, encapsulation is very useful for javascript!
So what is the purpose of implementing inheritance in javascript?
2.2 Why inheritance
In other object-oriented programming languages, in addition to reducing the writing of repeated code, the biggest use of inheritance is to achieve polymorphism. This is especially true in strongly typed languages:
1. In strongly typed languages, a variable cannot be assigned two values of different types unless the two types are compatible with the type of the variable. This compatible relationship is achieved through inheritance.
2. In a strongly typed language, methods of an existing type cannot be directly extended or rewritten. To extend a type, the only way is to inherit it and extend and rewrite it in its subclasses.
Therefore, for strongly typed object-oriented languages, the implementation of polymorphism depends on the implementation of inheritance.
For the JavaScript language, inheritance is not so important for achieving polymorphism:
1. In the JavaScript language, a variable can be assigned a value of any type, and any type can be called in the same way. Method with the same name on the object.
2. In the JavaScript language, methods of existing types can be directly expanded and rewritten through prototypes.
So, in JavaScript, the main role of inheritance is to reduce the writing of duplicate code.
The two methods of inheritance we are going to talk about next may be familiar to everyone. One is the prototypal inheritance method and the other is the call inheritance method. Neither of these two methods will produce side effects. We mainly discuss the essence of these two methods and what needs to be paid attention to.
2.3 Prototypal inheritance method
In JavaScript, each class (function) has a prototype, and the members of the prototype will be passed to the instantiated object of the 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 with the object as the prototype is instantiated, the members of the object will be passed to the instance of the class with which it is the prototype. on the object. This is the essence of prototypal inheritance.
Prototypal 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.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 is a good reflection of how to use prototypal inheritance. inherit.