Today, we discuss how to implement data encapsulation (encapsulation) in JavaScript scripts.
The simple point of data encapsulation is to hide the content that you do not want the caller to see. It is the first of three elements of object-oriented programming, the other two are inheritance and polymorphism, and their contents are discussed later.
About the implementation of data encapsulation, in C + +, Java, C # and other languages through the public, private, static and other keywords to achieve. In JavaScript, a different form is used. Before we discuss how to implement data encapsulation in some way, let's say a few simple, familiar but easily overlooked JavaScript concepts.
1 Several basic concepts
1.1 Variable Definitions
In the JavaScript language, variables are defined by the VAR keyword.
But if we assign a value directly to a variable that is not defined with VAR, the variable becomes a global variable.
In general, we should avoid using variables that are not defined with Var, mainly because it affects the execution efficiency of programs because accessing global variables is much slower than local variables.
But this usage ensures that our variables must be global variables.
In addition, in order to ensure the speed, we use the global variable, we can define a local variable Var, and then give the global variable, so you can get a global variable local reference.
1.2 Variable types
There are no defined variables, the type is undefined.
The value of a variable can be a function.
A function can act as a role in a class in JavaScript.
1.3 Variable Scopes
The scope of a variable is the valid range of the variable's life cycle.
A block created simply with {} cannot create a scope.
With the object scope that it contains is added to the current scope chain, but with does not create a new scope. When the With block ends, the object scope is removed from the current scope chain.
In Try-catch, a catch Error object is valid only in a catch block, but the variable defined in the catch block belongs to the current scope.
Other blocks created by control statements such as if, for, for-in, while, do-while, and switch cannot create scopes.
Functions created with function create a new scope to add to the current scope.
2 Package
Here we will discuss the specific encapsulation. First, let's say a few of the most familiar packages: private instance members, public instance members, and public static members. Finally, we will discuss the packaging of private static members and static classes that you are unfamiliar with. Because the next thing to discuss is object-oriented programming, all when a function is defined and used as a class, we will make it a class for the time being.
2.1 Private Instance Members
A private instance member can actually be implemented in JavaScript by a local variable within a function, which is equivalent to a private instance member of a class. For example:
Copy Code code as follows:
Class1 = function () {
Private fields
var m_first = 1;
var m_second = 2;
Private methods
function Method1 () {
alert (M_first);
}
var method2 = function () {
alert (M_second);
}
Constructor
{
Method1 ();
Method2 ();
}
}
var o = new Class1 ();
Error
alert (O.m_first);
O.method1 ();
Here M_first and M_second are Class1 's two private instance fields, Method1 and METHOD2 are two private instance methods. They can only be used within the object of the class and cannot be used outside of the object.
Here you will find that there are two ways to create a private method, one is to define the method directly in the class, the other is to define a local variable (the private instance field), and then define an anonymous method assignment to it.
Defining a method directly in a class, the scope of the method is this class, so the method cannot be accessed outside of the class, and it can access all the private instance fields in the class, which ensures that this is a private instance method.
The second way to create a private instance method is the same as the first, but the second way is more flexible.
You should also notice that the constructor code is enclosed in {} in Class1, which is not necessary, but the code looks clearer.
There are two more points to note about this constructor code:
1. The constructor code must be placed at the end of the entire class definition in order to ensure that the method that is invoked in it is already defined. Because JavaScript is an interpreted language, it executes in order from top to bottom, so if the constructor code is placed before the other method definition, an error occurs when the method is not found to be invoked when executing to the calling statement.
2, we already know that {} The blocks created will not change the scope, so if you create a local variable in such a constructor code, you actually create a private instance member in the entire class, so if you need to use a local variable, you should define a private instance method, such as constructor (), which can be named Constructor () This private instance method defines the local variable and the code to be executed in the original {} constructor, and then calls it directly at the end of the class. So the better way to do this is to:
Copy Code code as follows:
Class1 = function () {
Private fields
var m_first = 1;
var m_second = 2;
Private methods
function Constructor () {
Method1 ();
Method2 ();
}
function Method1 () {
alert (M_first);
}
var method2 = function () {
alert (M_second);
}
Constructor ();
}
var o = new Class1 ();
Error
alert (O.m_first);
O.method1 ();
Finally, you may also find that the definition of Class1 does not use Var, so we can guarantee that it is a global class.
2.2 Public Instance Members
Public instance members can be created in two ways, let's take a look at the following example:
Copy Code code as follows:
Class2 = function () {
Private fields
var m_first = 1;
var m_second = 2;
Private methods
function Method1 () {
alert (M_first);
}
var method2 = function () {
alert (M_second);
}
Public fields
This.first = "a";
This.second = [' s ', ' e ', ' C ', ' O ', ' n ', ' d '];
Public methods
This.method1 = METHOD2;
THIS.METHOD2 = function () {
alert (This.second);
}
Constructor
{
Method1 ();
Method2 ();
}
}
Public method
Class1.prototype.method3 = function () {
alert (This.first);
}
var o = new Class2 ();
O.method1 ();
O.METHOD2 ();
O.method3 ();
alert (O.first);
We found this example to be a supplement to the Class1 example. Add public instance fields and public instance methods to it, we call them public instance members.
We should have found that creating a public instance member is actually very simple, one way is to assign a value to This.membername in a class, if the value is a type other than a function, that is a public instance field, and if the value is a function type, that is the public instance method. Another way is by assigning values to ClassName.prototype.memberName, which is the same type of assignment as This.membername.
Is this the way it is defined, or is it defined by a prototype way of doing so?
In fact, they each have their own purposes, and they are not better than who the relationship. In some cases, we can only define public instance members in a particular way, rather than using a different approach. The reason is that they are actually different:
1, prototype mode should only be defined outside the class. This method can only be defined in the class.
2, prototype way if you define in a class, when you access a private instance member, you always access the private instance member in the last instance of the object.
3. Public instance members defined by the prototype method are members created on top of the prototype of the class. This method defines a public instance member that is directly created on the instance object of the class.
Based on the first two points, we can conclude that if you want to access a private instance member in a public instance method, you must define it in this way.
With regard to the 3rd distinction, we further analyze it later in the discussion of inheritance. It's OK to know the difference here.
We will also find that public instance members and private instance member names can be the same, so that there is no conflict?
Of course not. The reason is that they are accessed in a different way, and the public instance member must use this when accessing the class. Prefix to reference. When private instance members are accessed in a class, they are neither used nor able to use this. Prefix to access. When accessing outside the class, only public members can be accessed through instance objects of the class, and private members cannot access them.
2.3 Public static Members
The definition of a public static member is simple, for example:
Copy Code code as follows:
CLASS3 = function () {
Private fields
var m_first = 1;
var m_second = 2;
Private methods
function Method1 () {
alert (M_first);
}
var method2 = function () {
alert (M_second);
}
Constructor
{
Method1 ();
Method2 ();
}
}
public static field
Class3.field1 = 1;
public static method
Class3.method1 = function () {
alert (class3.field1);
}
Class3.method1 ();
The CLASS3 of this example is very similar to Class1. The difference is outside of CLASS3, and we define a static field and static method for CLASS3.
The way to define it is to assign a value directly to Classname.membername.
Static fields and static methods defined here can be accessed directly through the class name reference without the need to create the object. So they are public static members.
Remember, however, that it is important not to define a public static member within the class in which it resides, otherwise you will get a result that is not what you expect. We can look at the following example:
Copy Code code as follows:
CLASS4 = function () {
Private fields
var m_first = 1;
var m_second = 2;
var s_second = 2;
Private methods
function Method1 () {
alert (M_first);
}
var method2 = function () {
alert (M_second);
}
Class4.method1 = function () {
s_second++;
}
CLASS4.METHOD2 = function () {
alert (S_second);
}
}
var O1 = new Class4 ();
CLASS4.METHOD2 (); 2
Class4.method1 ();
CLASS4.METHOD2 (); 3
var O2 = new Class4 ();
CLASS4.METHOD2 (); 2
Class4.method1 ();
CLASS4.METHOD2 (); 3
In this example, we expect S_second to play the role of a private static member, but the output is not what we expect. We will find that S_second is actually a private instance member of CLASS4, not a private static member. The private member accessed by CLASS4 's method1 and METHOD2 is always the private instance member in the last instance object of the class.
What's the problem?
The problem is that when an object instance is created with the new Class4 (), all the statements in CLASS4 are executed again, so S_second is reset and becomes a private instance member in the new object. and CLASS4.METHOD1 and CLASS4.METHOD2 are redefined, and this definition switches their variable scopes to the last object. This is the same as the error that is generated by defining the public instance method created in the prototype way inside the class.
So, be sure not to define the public static members within the class in which they reside! Also, do not define the public instance method created by the prototype way inside the class!
So how do you define a private static member?
2.4 Private static Members
Earlier in the basic concept we already know that only function creation can create a new scope, and to create a private member (whether static or instance), you need to create a new scope to be able to play the purpose of data hiding. The following approach is implemented based on this.
Implementing a private static member is accomplished by creating an anonymous function function to create a new scope.
Usually when we use an anonymous function, we assign it to a variable and then reference the anonymous function through that variable. In this case, the anonymous function can be invoked repeatedly or as a class to create the object. And here, the anonymous function we create is not assigned to any variable, executes immediately after it is created, or is immediately instantiated as an object, and the object is not assigned to any variable, in which case the function itself or its instantiated object cannot be accessed again. So its only function is to create a new scope and isolate all local variables and functions within it. Therefore, these local variables and functions are the private static members we need. And this immediately executed anonymous function, or immediately instantiated anonymous function, we call it a static encapsulation environment.
Let's take a look at the example of creating a class with a private static member by directly calling the anonymous function:
Copy Code code as follows:
CLASS5 = (function () {
private static fields
var s_first = 1;
var s_second = 2;
private static methods
function S_method1 () {
s_first++;
}
var s_second = 2;
function Constructor () {
Private fields
var m_first = 1;
JavaScript Object-oriented new practice (II.)
var m_second = 2;
Private methods
function Method1 () {
alert (M_first);
}
var method2 = function () {
alert (M_second);
}
Public fields
This.first = "a";
This.second = [' s ', ' e ', ' C ', ' O ', ' n ', ' d '];
Public methods
This.method1 = function () {
s_second--;
}
THIS.METHOD2 = function () {
alert (This.second);
}
Constructor
{
S_method1 ();
This.method1 ();
}
}
public static methods
Constructor.method1 = function () {
s_first++;
alert (S_first);
}
CONSTRUCTOR.METHOD2 = function () {
alert (S_second);
}
return constructor;
})();
var O1 = new Class5 ();
Class5.method1 ();
CLASS5.METHOD2 ();
O1.METHOD2 ();
var O2 = new Class5 ();
Class5.method1 ();
CLASS5.METHOD2 ();
O2.METHOD2 ();
In this example, by
(function () {
...
function Contructor () {
...
}
return constructor;
})();
To create a static encapsulation environment, the actual class is defined in this environment, and the final class is returned to our global variable CLASS5 through the return statement, and then we can refer to the class with the static private member by CLASS5.
In order to differentiate between private static members and private instance members, we use the S_ prefix in front of the private static member, with the m_ prefix in front of the private instance member, thus avoiding the duplicate name, so that private static members can always be accessed in the object.
But this naming is not necessary, it's just recommended, private static members can have the same name as private instance members, and in the case of duplicate names, private instance members are accessed in the class constructor and in the instance methods defined in the class. Private static members are accessed in static methods, whether public static or private static methods.
Private static members are accessed by public instance methods that are defined outside the class and in a static encapsulation environment by prototype way.
public static methods defined outside the static encapsulation environment and public instance methods defined by prototype methods cannot directly access private static members.
Another way to create a class with a private static member by directly instantiating an anonymous function is similar to the example above:
Copy Code code as follows:
New function () {
private static fields
var s_first = 1;
var s_second = 2;
private static methods
function S_method1 () {
s_first++;
}
var s_second = 2;
CLASS6 = function () {
Private fields
var m_first = 1;
var m_second = 2;
Private methods
function Method1 () {
alert (M_first);
}
var method2 = function () {
alert (M_second);
}
Public fields
This.first = "a";
This.second = [' s ', ' e ', ' C ', ' O ', ' n ', ' d '];
Public methods
This.method1 = function () {
s_second--;
}
THIS.METHOD2 = function () {
alert (This.second);
}
Constructor
{
S_method1 ();
This.method1 ();
}
}
public static methods
Class6.method1 = function () {
s_first++;
alert (S_first);
}
CLASS6.METHOD2 = function () {
alert (S_second);
}
};
var O1 = new Class6 ();
Class6.method1 ();
CLASS6.METHOD2 ();
O1.METHOD2 ();
var O2 = new Class6 ();
Class6.method1 ();
CLASS6.METHOD2 ();
O2.METHOD2 ();
The result of this example is the same as the example created in the first way. But its static packaging environment is like this:
New function () {
...
};
Here, the function does not return a value, and the definition of CLASS5 is implemented directly within the static encapsulation environment by assigning a value to a variable that is not defined with Var.
Of course, it is also perfectly possible to
(function () {
...
})();
In this way, you do not define a return value for the function, and you implement the definition of a class with a private static member directly within the static encapsulation environment by assigning a value to a variable that is not defined with Var.
Both of these methods are equivalent here.
2.5 Static Class
The so-called static class is a class that cannot be instantiated and contains only static members.
In JavaScript we can implement static classes by directly instantiating an object that is an anonymous function. For example:
Copy Code code as follows:
CLASS7 = new function () {
private static fields
var s_first = 1;
var s_second = 2;
private static method
function Method1 () {
alert (S_first);
}
public static method
This.method1 = function () {
Method1 ();
alert (S_second);
}
}
Class7.method1 ();
You will find that CLASS7 is actually an object, except that this object belongs to an anonymous class that, after creating the object of CLASS7, cannot be used anymore. CLASS7 is not a function, so it cannot be instantiated as a class, so here it is equivalent to a static class.