JavaScript is widely used today, and various applications depend on it daily. Web programmers have gradually become accustomed to using a variety of excellent JavaScript frameworks to quickly develop Web applications, thus ignoring the learning and in-depth understanding of native JavaScript. Therefore, it is often the case that many programmers who have developed JavaScript for many years cannot clearly understand closures, functional programming, and prototypes. Even if the framework is used, its code organization is also very bad. This is a manifestation of insufficient understanding of native JavaScript language features. To master JavaScript, first of all, we must discard the interference of some other advanced languages such as Java, C #, and other class-oriented Object-oriented thinking, fully understand the features of the original JavaScript object-oriented model from the perspective of functional language. After grasping this point, it is possible to use this language further. This article is suitable for: programmers who have used the JS framework but lack an understanding of the nature of the JS language, have development experience in Java, C ++ and other languages, and are ready to learn and use JavaScript, and JavaScript fans who have been vague about whether JavaScript is object-oriented, but want to know the truth.
New Understanding of object-oriented
To demonstrate that JavaScript is a thorough object-oriented language, it is necessary to start with the object-oriented concept and discuss several concepts of object-oriented:
Everything is an object
Objects are encapsulated and inherited.
Communication is used between objects to hide information.
Based on these three points, C ++ is a semi-object-oriented semi-process language, because although it implements class encapsulation, inheritance, and polymorphism, however, there are non-object global functions and variables. Java and C # are completely object-oriented languages. They organize functions and variables in the form of classes so that they cannot be separated from objects. But here the function itself is a process, only attached to a class.
However, object-oriented is just a concept or programming idea. It should not depend on a language. For example, Java uses the object-oriented idea to construct its language, which implements mechanisms such as class, inheritance, derivation, polymorphism, and interface. However, these mechanisms are only a means to implement object-oriented programming, rather than necessary. In other words, a language can choose an appropriate method based on its own characteristics to implement object-oriented. Therefore, most programmers first learn or use advanced compilation languages such as Java and C ++ (although Java is semi-compiled and semi-interpreted, it is generally explained as a compilation type ), therefore, the "class" Object-Oriented implementation method is accepted first, so that when learning the script language, it is customary to use the concepts in class-based object-oriented language to determine whether the language is an object-oriented language or whether it has the object-oriented feature. This is one of the important reasons that impede programmers from learning and mastering JavaScript.
In fact, the JavaScript language implements object-oriented programming through a prototype method. Next we will discuss the differences between class-based object-oriented methods and prototype-based Object-Oriented Methods in constructing the objective world.
Comparison between class-based and prototype-Based Object-Oriented Methods
In the class-based object-oriented approach,Object (Object)Rely onClass (Class). In the prototype-based object-oriented approach,Object (Object)Depends onConstructor (Constructor)ExploitationPrototype (Prototype)Constructed. Here is an example of the objective world to illustrate the differences between the two methods of cognition. For example, a factory creates a car. On the one hand, a worker must refer to an engineering drawing to design how the car should be made. The engineering drawings here are likeClass (Class)And the car follows thisClass (Class)Manufacturing; on the other hand, workers and machines (equivalent to constructor) use various components, such as engines, tires, and steering wheel (equivalent to the prototype attributes) to construct the car.
As a matter of fact, there is still debate about who expresses the idea of object-oriented more thoroughly. However, I believe that the original type of object-oriented method is a more thorough object-oriented method, for the following reasons:
First of all, objects in the objective world are produced by the construction of other objects. Abstract "drawings" cannot produce "cars". That is to say, A class is an abstract concept rather than an entity, and an object is generated;
Secondly, according to the most basic object-oriented rule that everything is an object, the class itself is not an object. However, constructor in the prototype method) prototype is also an object constructed by other objects in prototype mode.
Thirdly, in the class-Oriented Object Language, the object state is held by the object instance, and the object behavior method) it is held by the class that declares the object, and only the structure and method of the object can be inherited. In the original type object-oriented language, the behavior and status of the object belong to the object itself, and can be inherited together (reference resources), which is closer to objective reality.
Finally, in order to make up for the inconvenience of using global functions and variables in the process-oriented language, the Class-oriented object-oriented language such as Java allows you to declare static attributes and static methods in the class. In fact, there is no static concept in the objective world, because everything is an object! In the original type object-oriented language, in addition to the built-in object, the existence of global objects, methods, or attributes is not allowed and there is no static concept. All language elements (primitive) must depend on objects. However, due to the features of functional language, the language elements depend on objects that change with the context of the runtime. this is reflected in the changes of the this pointer. It is precisely this kind of feature that is closer to the natural viewpoint that "Everything belongs, and the universe is the foundation of the survival of everything. In program list 1WindowSimilar to the concept of the universe.
Listing 1. Context dependency of an object
Copy codeThe Code is as follows:
<Script>
Var str = "I Am A String object. I declare it here, but I do not exist independently! "
Var obj = {des: "I am an Object. I declare that it does not exist independently. "};
Var fun = function (){
Console. log ("I Am a Function object! Who calls me, who I belong to: ", this );
};
Obj. fun = fun;
Console. log (this = window); // print true
Console. log (window. str === str); // print true
Console. log (window. obj = obj); // print true
Console. log (window. fun === fun); // print true
Fun (); // print I am a Function object! Who calls me, who I belong to: window
Obj. fun (); // print I am a Function object! Who calls me, who I belong to: obj
Fun. apply (str); // print I am a Function object! Who calls me, who I belong to: str
</Script>
After accepting the fact that there is a prototype-based implementation method for Object-Oriented objects, we can discuss in depth how ECMAScript constructs its own language based on this method.
Basic object-oriented
ECMAScriptIt is a thoroughly object-oriented programming language (resource reference), and JavaScript is a variant of it (variant ). It provides six basic data types: Boolean, Number, String, Null, Undefined, and Object. To implement object-oriented,ECMAScriptA very successful data structure-JSON (JavaScript Object Notation ), this classic structure can be separated from the language and become a widely used data interaction format (reference resources ).
It should be said that ECMAScript with basic data types and JSON construction syntax can basically implement object-oriented programming. Developers can freely useLiteral Statement (Literal notation)To construct an object and assign values to its non-existing attributes, or delete the attribute using delete (Note: The delete keyword in JS is used to delete the object attribute, is often mistakenly used as the delete in C ++, and the latter is used to release objects that are no longer used), such as program list 2.
Listing 2. literal notation object Declaration
Copy codeThe Code is as follows:
Var person = {
Name: "Zhang San ",
Age: 26,
Gender: "male ",
Eat: function (stuff ){
Alert ("I am eating" + stuff );
}
};
Person. height = 176;
Delete person ["age"];
In the actual development process, most beginners or developers who do not have high requirements for JS applications can simply use this part of content defined by ECMAScript to meet basic development requirements. However, such code reusability is very weak, and it is somewhat dry compared with other class-oriented object-oriented strong language that implements inheritance, derivation, polymorphism, and so on, cannot meet complex JS application development requirements. Therefore, ECMAScript introduces prototype to solve the Object Inheritance Problem.
Construct an object using the function Constructor
BesidesLiteral Statement (Literal notation)In addition to the method, ECMAScript allowsConstructor)Create an object. Each constructor is actuallyFunction (Function) ObjectThe function object contains a prototype attribute for implementation.Prototype-based inheritance(Prototype-based inheritance)AndShared Property (Shared properties).The object can be created by using the "new Keyword + constructor call" method, such as program listing 3:
Listing 3. Using constructor to create an object
Copy codeThe Code is as follows:
// The constructor Person is a function object.
Function Person (){
// Some initialization work can be done here
}
// It has a property named prototype.
Person. prototype = {
Name: "Zhang San ",
Age: 26,
Gender: "male ",
Eat: function (stuff ){
Alert ("I am eating" + stuff );
}
}
// Use the new keyword to construct an object
Var p = new Person ();
Since the inventor of JavaScript in the early days, in order to bring the language to the famous Java language (although we know that the two are the relationship between Lei Feng and Lei Feng ), the new keyword is used to restrict the constructor to call and create an object, so that it looks similar in syntax to the way Java creates an object. However, it should be noted that the new meanings of these two languages are irrelevant because the object construction mechanism is completely different. It is precisely because the syntax is similar here that many programmers who are used to the object creation method in class-Oriented Object language cannot fully understand the method of JS object prototype construction, because they always don't understand Why "function names can be class names" in JS language. In essence, JavaScript only borrows the keyword "new". In other words, ECMAScript can use other non-new expressions to call the constructor to create objects.
A thorough understanding of prototype chain)
In ECMAScript, each object created by the constructor has a property value pointing to the constructor prototype.Implicit reference (Implicit reference)This reference is calledPrototype (Prototype). Further, each prototype can haveImplicit reference(That is, the prototype of the prototype ).Prototype chain (Prototype chain)(Refer to resources ). In a specific language implementation, each object has_ Proto _ attributesTo implement the prototypeImplicit reference. Program list 4 illustrates this.
Listing 4. Object _ proto _ attributes and implicit references
Copy codeThe Code is as follows:
Function Person (name ){
This. name = name;
}
Var p = new Person ();
// The implicit reference of the object points to the prototype attribute of the constructor, so print true here
Console. log (p. _ proto _ = Person. prototype );
// The prototype itself is an Object, so its implicit reference points
// Print the prototype attribute of the Object constructor to true.
Console. log (Person. prototype. _ proto _ = Object. prototype );
// The constructor Person itself is a function object, so print true here
Console. log (Person. _ proto _ = Function. prototype );
WithPrototype chainTo define a so-calledProperty hiding MechanismAnd implement inheritance through this mechanism. ECMAScript specifies that when an object attribute is assigned a value, the interpreter searches for the first object in the prototype chain of the object that contains this attribute (Note: The prototype itself is an object, the prototype chain is the chain of a group of objects. The first object in the prototype chain of an object is the object itself. Otherwise, if you want to obtain the value of an object property, the interpreter naturally returns the object property value that first has this property in the prototype chain of the object. Figure 1 shows the hidden mechanism:
Figure 1. Property hiding mechanism in the prototype chain
In Figure 1, object1-> prototype1-> prototype2 forms the prototype chain of the object object1. Based on the property hiding mechanism described above, we can clearly see that the property4 attribute in prototype1 object and the property3 attribute in prototype2 object are hidden. After understanding the prototype chain, it is very easy to understand the prototype-based inheritance implementation principle in JS. program list 5 is a simple example of using the prototype chain to implement inheritance.
Listing 5. Use the prototype chain Horse-> Mammal-> Animal to implement inheritance
Copy codeThe Code is as follows:
// Declare the Animal object constructor
Function Animal (){
}
// Point the prototype attribute of Animal to an object,
// It can also be understood as the prototype of the specified Animal object.
Animal. prototype = {
Name: animal ",
Weight: 0,
Eat: function (){
Alert ("Animal is eating! ");
}
}
// Declare the Mammal object constructor
Function Mammal (){
This. name = "mammal ";
}
// Specify the prototype of the Mammal object as an Animal object.
// In fact, this is the prototype chain between creating a Mammal object and an Animal object.
Mammal. prototype = new Animal ();
// Declare the Horse object constructor
Function Horse (height, weight ){
This. name = "horse ";
This. height = height;
This. weight = weight;
}
// Specify the prototype of the Horse object as a Mamal object and continue to build the prototype chain between the Horse and Mammal.
Horse. prototype = new Mammal ();
// Re-specify the eat method. This method will overwrite the eat method inherited from the Animal prototype.
Horse. prototype. eat = function (){
Alert ("Horse is eating grass! ");
}
// Verify and understand the prototype chain
Var horse = new Horse (100,300 );
Console. log (horse. _ proto _ = Horse. prototype );
Console. log (Horse. prototype. _ proto _ = Mammal. prototype );
Console. log (Mammal. prototype. _ proto _ = Animal. prototype );
The key to understanding the implementation of the Object prototype inheritance logic in listing 5 is the code of Horse. prototype = new Mammal () and Mammal. prototype = new Animal. First, the result on the right of the equation is to construct a temporary object, and then assign this object to the prototype attribute of the object on the left of the equation. That is to say, the new object on the right is used as the prototype of the object on the left. The reader can replace these two equations with the corresponding program list. 5. The final two-line equations of the code can be self-understood.
Implementation of JavaScript class inheritance
From the code list 5, we can see that although the prototype-based inheritance method achieves code reuse, It is loose and not fluent, and has poor readability, it is not conducive to implementation of expansion and effective organization and management of source code. I have to admit that the class inheritance method is more robust in language implementation and has obvious advantages in building reusable code and organizational architecture programs. This allows programmers to find a way to encode JavaScript in the class inheritance style. From an abstract point of view, since both class inheritance and prototype inheritance are designed to achieve object-oriented design, and their respective carrier languages are equivalent in terms of computing power (because the computing power of the Turing machine is equivalent to that of the Lambda algorithm), can we find a transformation, so that the original type inheritance language can implement the class inheritance encoding style through this transformation?
Currently, some mainstream JS frameworks provide such conversion mechanisms, that is, class declaration methods, such as Dojo. declare () and Ext. entend. Using these frameworks, you can easily and amicably organize your own JS Code. In fact, before the emergence of many frameworks, JavaScript mastersDouglas CrockfordAt first, we used three functions to expand the Function object and implemented this transformation. For details about its implementation, see resources ). In additionDean EdwardsImplementation of the famous Base. js (reference resources ). It is worth mentioning that the father of jQueryJohn ResigWith less than 30 lines of codeSimple Inheritance. Using the extend method provided by OSS to declare a class is very simple. Program list 6 is usedSimple InheritanceLibrary implementation class declaration example. The last print output statement isSimple InheritanceThe best way to implement class inheritance.
Listing 6. Use Simple Inheritance to implement class Inheritance
Copy codeThe Code is as follows:
// Declare the Person class
Var Person = Class. extend ({
_ Issleeping: true,
Init: function (name ){
This. _ name = name;
},
IsSleeping: function (){
Return this. _ issleeping;
}
});
// Declare the Programmer class and inherit the Person
Var Programmer = Person. extend ({
Init: function (name, issleeping ){
// Call the parent class Constructor
This. _ super (name );
// Set your own status
This. _ issleeping = issleeping;
}
});
Var person = new Person ("James ");
Var diors = new Programmer ("Zhang Jiangnan", false );
// Print true
Console. log (person. isSleeping ());
// Print false
Console. log (diors. isSleeping ());
// Print true because all values are true.
Console. log (person instanceof Person & person instanceof Class
& Diors instanceof Programmer &&
Diors instanceof Person & diors instanceof Class );
If you have a full understanding of the prototype, function constructor, closure, and context-based this, it is not quite difficult to understand the implementation principle of Simple Inheritance. In essence, var Person = Class. extend (...) in this statement, the Person on the left actually obtains a constructor returned by Class calling the extend method, that is, a reference to a function object. Following this train of thought, we will continue to introduce how Simple Inheritance achieves this, and then implement the conversion from prototype Inheritance to class Inheritance. Figure 2 shows the source code of Simple Inheritance and its accompanying annotations. For ease of understanding, add instructions to the code line by line in Chinese.
Figure 2. Simple Inheritance source code parsing
Aside from the second part of the code, the first part and the third part of the code consistently found that the fundamental purpose of the extend function is to construct a new constructor with new original attributes. We can't help but sighJohn ResigMaster's handwriting and its delicate grasp of the essence of JS language. AsJohn ResigIt is how to think of such a subtle implementation method. Interested readers can read this article (refer to resources), which details the thinking process of the initial design of Simple Inheritance.
JavaScript private member implementation
So far, if you are skeptical about JavaScript object-oriented, this suspicion must be that JavaScript has not implemented information hiding in object-oriented systems, that is, private and public. Unlike other class-oriented objects that explicitly declare private public members, JavaScript Information Hiding relies on closures. See program list 7:
Listing 7. Using closures to hide information
Copy codeThe Code is as follows:
// Declare the User Constructor
Function User (pwd ){
// Define private attributes
Var password = pwd;
// Define a private Method
Function getPassword (){
// The password in the closure is returned.
Return password;
}
// Privileged function declaration, used for other public methods of the object to access private members through this privileged method
This. passwordService = function (){
Return getPassword ();
}
}
// Public member declaration
User. prototype. checkPassword = function (pwd ){
Return this. passwordService () === pwd;
};
// Verify the concealment
Var u = new user( "123456 ");
// Print true
Console. log (u. checkPassword ("123456 "));
// Print undefined
Console. log (u. password );
// Print true
Console. log (typeof u. gePassword = "undefined ");
JavaScript must rely on closures to hide information, which is determined by its functional language features. This article will not discuss Functional Languages and closures, just as you have understood the context-based this in JavaScript by default. For information hiding in JavaScript,Douglas CrockfordThe article "Private members in JavaScript" (refer to resources) provides more authoritative and detailed introduction.
Conclusion
JavaScript is considered to be the most misunderstood programming language in the world, because it is covered by the c-language family, it represents the functional language features of the LISP style; no class, but it has completely implemented object-oriented. To have a thorough understanding of the language, you must open the coat of its c language, return to the functional programming perspective, and discard the object-oriented concept of the original class to learn and understand it. With the popularity of Web applications and the rapid development of JS language in recent years, especially the emergence of backend JS engines (such as NodeJS Based on V8), we can foresee that, originally, JS, which is just used as a page for compiling toys, will gain a broader development world. Such a development trend also puts forward higher requirements for JS programmers. Only by thoroughly understanding this language can we exert her power in a large JS project.