Javascript| Programming | objects | inheritance
We'll show you how JavaScript implements the object-oriented language: inheritance . At the same time, these examples will show you how to implement the encapsulation of the class. Here, we will not discuss polymorphic implementations.
While JavaScript is a scripting language, the object-oriented programming it supports is also very powerful. Although it has no classes and instances, it has objects, prototypes, and implicit inheritance. We will explain how to simulate the form of inheritance and the relationship between its superclass and subclasses. The prototype is the key to understanding the concept of inheritance, and we will teach you how to build prototypes, how to detect whether an object is a prototype of another object, and the difference between its JavaScript model and Java object-oriented programming. We will also show you how to detect the various properties that an object contains. In another article, I will also explain in detail the " prototype chain (prototype chain)" knowledge.
This article has extensively consulted the contents of "object-oriented programming with JavaScript and part i:inheritance" in webreference.com , many Allow me to carry out a detailed test and further discussion to ensure that the content will not be too much error.
Original address: http://www.webreference.com/js/column79/
The characteristics of object-oriented language
Object-oriented design is based on the following 3 main principles: encapsulation, inheritance, and polymorphism. Say that a program language is designed to support OO (object-oriented), and that it is only possible to support the above 3 concepts in its syntax. This language should provide you with some means to make it easy to define and use these examples. Encapsulation involves the idea of turning an object into a "black box." When you use an object, you don't have to know how it works inside, and you don't have to understand how the object works. This object simply provides the information that is absolutely useful in an interface way. This object should provide you with a friendly interface so that you can use its limited set of properties and methods. Encapsulation also means that an object contains everything it needs, which includes data and operations on it. The concept of encapsulation is powerful because it allows a large software project to be effectively assigned to each developer, and for everyone on the team, they only need to focus on the object they are implementing without having to focus too much on the implementation of others. The cost of development projects increases the number of members and interfaces in the development team exponentially. Encapsulation is the most popular OO design concept since the "software Crisis".
The reuse of software is another important feature of OO design idea. The main way to implement this idea in a software system is to inherit. A class is a function that defines an object. A superclass is a new class, or a subclass of a source class that is built. A subclass inherits its methods and properties from its superclass. In fact, all subclasses are automatically generated, thus saving a lot of work. You don't need to define these subclasses one by one. Of course, you can overload the inherited methods and attributes. In fact, no one has pointed out which subclass is going to be exactly the same as its superclass, unless you have not overloaded any of the properties and methods.
Polymorphism is probably the most complex of the 3 concepts. Essentially, each object can handle a variety of different data types. You don't have to create different classes to handle different types of data. The typical example is the drawing class, and you don't have to write a different class for the implementation of the circle, the rectangle, and the ellipse. You can create a class that is smart enough to invoke a specific method to manipulate a particular shape.
Implementing Inheritance through functions
Although JavaScript does not support displaying inheritance operators, you can implement implicit inheritance in a way that you do. There are 2 more common ways to implement the inheritance of a class. The first way to define a class as a subclass is to call the superclass's constructor in the interior of the constructor that is responsible for defining the child class function. Look at the following example:
Super class constructors
Function Superclass () {
This.bye = Superbye;
This.hello = Superhello;
}
Child class constructor
function Subclass () {
This.inheritfrom = superclass;
This.inheritfrom ();
This.bye = Subbye;
}
function Superhello () {
Return "Hello from superclass";
}
function Superbye () {
Return to "Bye from superclass";
}
function Subbye () {
Return to "Bye from subclass";
}
function to test a constructed attribute
function Printsub () {
var newclass = new Subclass ();
Alert (Newclass.bye ());
Alert (Newclass.hello ());
}
When you run the Printsub function above, it executes the subbuy and Superhello functions sequentially. As we can see, the bye and Hello methods were first defined in superclass. However, in subclass, the bye method is overloaded again, and the function of the first two lines of the subclass constructor only makes a simple primitive inheritance operation, but it is an inherited operation done by showing the execution of the Inheritfrom method. The inherited process first assigns the Superclass object prototype to the Inheritfrom method under subclass, and then after the superclass constructor is executed, the superclass property is automatically added to the Subclass property list. This Primarily because of the inheritfrom (that is, the superclass) constructor invoked through this in subclass, when the superclass constructor is invoked in this way, the JavaScript interpreter puts the superclass This in subclass is understood as this keyword in the same scope, so it has an inheritance effect.
In addition, it should be noted that for any instantiated object, you arbitrarily add attributes or methods to it, as follows:
var newclass = new Subclass ();
Newclass.addprop = "Added property to instance object";
Obviously, the properties and methods added in this way are valid only for the current instantiated object and do not affect all of the same object instances. Undoubtedly, it is a unique object instance that you create.
Implementing Inheritance through prototypes
The second, and more powerful, method is to establish the inheritance of subclasses by creating a superclass object and assigning it to the prototype property of the subclass object. Suppose our superclass is superclass and the subclass is subclass. Its prototype assignment format is as follows:
Subclass.prototype = new Superclass;
For the implementation of the prototype inheritance, let's just rewrite the previous code, as shown in the following example:
Super class constructors
Function Superclass () {
This.bye = Superbye;
This.hello = Superhello;
}
Child class constructor
function Subclass () {
This.bye = Subbye;
}
Subclass.prototype = new Superclass;
function Superhello () {
Return "Hello from superclass";
}
function Superbye () {
Return to "Bye from superclass";
}
function Subbye () {
Return to "Bye from subclass";
}
function to test a constructed attribute
function Printsub () {
var newclass = new Subclass ();
Alert (Newclass.bye ());
Alert (Newclass.hello ());
}
As we can see, there is no other change than the first 2 lines in subclass in the previous one, except for the prototype assignment statements outside the function, but the execution of the code is the same as before.
Add properties to an already established object
Inheritance through a prototype is better than inheritance through a function implementation, because it supports dynamic inheritance. You can define other methods and properties of the superclass through the prototype property after the constructor has been completed, and the subclass object under it automatically obtains new methods and properties. Here's an example, and you can see how it works.
Function Superclass () {
This.bye = Superbye;
This.hello = Superhello;
}
function Subclass () {
This.bye = Subbye;
}
Subclass.prototype = new Superclass;
function Superhello () {
Return "Hello from superclass";
}
function Superbye () {
Return to "Bye from superclass";
}
function Subbye () {
Return to "Bye from subclass";
}
var newclass = new Subclass ();
/*****************************/
Dynamically added Blessyou properties
SuperClass.prototype.blessyou = superblessyou;
function Superblessyou () {
Return "Bless to Superclass";
}
/*****************************/
function Printsub () {
Alert (Newclass.bye ());
Alert (Newclass.hello ());
Alert (newclass.blessyou ());
}
This is what we often see as the technique of adding other properties and methods to internal objects, such as String, Math, and so on. For any internal object or custom object, you can also overload its properties and methods by prototype. Then when the call executes, it invokes the method and the property you defined. Here is an example:
Add a method for an internal String object
String.prototype.myMethod = function () {
Return "I define method";
}
Overloading a method for an internal String object
String.prototype.toString = function () {
Return "I define ToString method";
}
var myobj = new String ("foo");
Alert (Myobj.mymethod ());
alert (myobj);
Alert ("foo". toString ());
Also note that all JavaScript internal pairs of prototype properties are read-only. You can add or overload properties and methods for the prototype of an internal object as described above, but you cannot change the prototype prototype of the inner object. However, custom objects can be assigned to a new prototype. In other words, it's not interesting to do something like this below.
function Employee () {
this.dept = "HR";
This.manager = "John Johnson";
}
String.prototype = new Employee;
var myString = new String ("foo");
The above program will not make an error after running, but obviously, if you call Mystring.dept, you will get a value that is not defined.
In addition, one frequently used is the isprototypeof () method under prototype, which is used primarily to determine whether the specified object exists in the prototype chain of another object. The syntax is as follows:
Object1.prototype.isPrototypeOf (0BJECT2);
The above format is used to determine whether OBJECT2 appears in the prototype chain of Object1. Examples are as follows:
function person () {
THIS.name = "Rob Roberson";
This.age = 31;
}
function Employee () {
this.dept = "HR";
This.manager = "John Johnson";
}
Employee.prototype = new Person ();
var Ken = new Employee ();
When performing Employee.prototype.isPrototypeOf (Ken), Person.prototype.isPrototypeOf (Ken) and Object.prototype.isPrototypeOf ( Ken), the result returns TRUE.
For specific inheritance detection under Netscape
In Netscape Browser 4.x to 6, and in its Mozilla series browsing, JavaScript stores the prototype relationship between objects in a special internal Property object, __proto__ (2 underscores). Here is an example:
function Shape () {
This.borderwidth = 5;
}
function Square () {
This.edge = 12;
}
Square.prototype = new Shape;
Mypicture = new Square;
alert (mypicture.__proto__);
alert (mypicture.borderwidth);
Because the script executes the Square.prototype = new Shape statement, Mypicture has an internal property __proto__ that points to the Shape object. During the execution of a script, when you want to get an object's property value, and this object is an object created by a prototype assignment, the JavaScript parser looks at its __proto__ Property object and its prototype object when it does not define a property itself. It then enumerates all the attributes in its prototype, and the resulting result is either the attribute or the absence of the attribute. Without this attribute, the prototype object below the prototype object is enumerated until the process really ends. And all of these JavaScript engine internal operations, we do not know, the following is the explanation of the problem.
In fact, for all custom objects, it has a __proto__ internal object, regardless of whether it has used prototype assignment operations. And if an object is "inherited" through multiple layers of prototype, all "inherited" attributes can be traversed through a simple loop, without the need for recursive algorithms, because the JavaScript engine does it automatically. Examples are as follows:
function Shape () {
This.borderwidth = 5;
}
function Square () {
This.edge = 12;
}
function Roundsquare ()
{
This.radio = 0.5;
}
Square.prototype = new Shape;
Roundsquare.prototype = new Square;
var mypicture = new Roundsquare;
For (property in mypicture.__proto__) {
Alert (property);
}
We can also iterate through all the attributes inherited by a subclass object by changing the following loops:
For (property in Roundsquare.prototype) {
Alert (property);
}
If you're not afraid of trouble, we can even take out the original property values defined in its constructor in a cascading way.
alert (mypicture.__proto__.__proto__.borderwidth);
Regardless of whether you have modified this property value, the value of the property taken out of the above statement is the original defined value. Let's go down this line of thinking, and the following code involves another problem, which is related to the prototype chain (prototype chain). The code is as follows:
function State () {
}
Function City () {
}
City.prototype = new State;
Function Street () {
}
Street.prototype = new City;
var universityavenue = new Street ();
function Tryit () {
Alert (universityavenue.__proto__== street.prototype);
Alert (universityavenue.__proto__.__proto__==
City.prototype);
Alert (universityavenue.__proto__.__proto__.__proto__
= = State.prototype);
Alert (universityavenue.__proto__.__proto__.__proto__.
__proto__== Object.prototype);
Alert (universityavenue.__proto__.__proto__.__proto__.
__proto__.__proto__== null);
}
When the Tryit function is executed, all displays are true. In other words, the prototype.__proto__ of a subclass object is always equal to the prototype attribute of the superclass object; The prototype.__proto__ of Super class objects always equals Object.prototype; OBJECT.PROTOTYPE.__PROTO__ is always null; The __proto__ of an instance object is always equal to the prototype of its class object, which is why any custom object has a __proto__ attribute. For the above description, the corresponding code is as follows:
street.prototype.__proto__ = = City.prototype/True
state.prototype.__proto__ = = Object.prototype/True
OBJECT.PROTOTYPE.__PROTO__ = = NULL//True
universityavenue.__proto__ = = Street.prototype/True
Simulating the implementation of instanceof functions
Based on the contents of the previous section, we learned about the __proto__ features supported by Netscape. In this section, we will use this feature to create our own instance object detection functions.
Many times, we all need to determine whether an object is defined by a class, and in other languages, you can use the instanceof function to achieve this judgment. A instanceof run is also provided in JavaScript, and on the basis of __proto__, we can define the same function on our own, although it appears to be a duplication of effort, but it helps us to understand more deeply about __proto__ knowledge. The following code is only used to illustrate the function, in the actual application, you do not need to repeatedly define the instanceof function, you can use the instanceof operator.
function instanceof (object, constructorfunction) {
While (object!= null) {
if (object = = Constructorfunction.prototype)
{return true}
object = object.__proto__;
}
return false;
}
function State () {
}
Function City () {
}
City.prototype = new State;
Function Street () {
}
Street.prototype = new City;
var universityavenue = new Street ();
Function Demo () {
Alert ("Instanceof (Universityavenue, street) is" +
Instanceof (Universityavenue, Street));
Alert ("instanceof (universityavenue) is" +
Instanceof (Universityavenue, city));
Alert ("instanceof (universityavenue) is" +
Instanceof (Universityavenue, state));
}
You will see that all of the running results are true, and the principle is the same as the level of the previous section. It is proved that its running result is consistent with the running result of the instanceof operator.
You can detect the superclass of any object by using the constructor property, which returns the constructor called when a new object is created with the operator, and the return value is a function object type. Because object internal objects are supported by the constructor property, and some objects (both internal and custom) are inherited by object, all objects support this property. Let's take another look at the following example:
function Employee () {
this.dept = "HR";
This.manager = "John Johnson";
}
function Printprop () {
var Ken = new Employee ();
alert (ken.constructor);
}
After you call the Printprop function, you will see the definition text of the Employee function displayed in the pop-up box, in fact the Ken.constructor return value itself is a function object type, and the ToString method is implicitly invoked at alert . For the class object itself, you can also call Prototype.constructor to take out its constructor.
Sorting and printing of objects
JavaScript supports 3 main types of objects: internal objects, host objects, custom objects, and possibly special external objects, such as ActiveX objects or XPCOM objects. Internal objects are supported by the JavaScript language itself, such as Object, Math, number object, and so on. The common feature of all internal objects is that they start with uppercase letters, and they are case sensitive. If you want to use mathematical constant PI, you have to write Math.PI, if you write math. PI, JavaScript will show an error. Host objects are supported by browsers in order to be able to interact with the browsed documents, such as document, window, and frames. The host object is characterized by all objects starting with lowercase letters. Because JavaScript itself is case sensitive, you can't confuse the case. The only thing left to say is a custom object, and you can define your object in lowercase or case, but it must conform to the basic naming conventions. As shown below, this is a custom object:
function Employee () {
this.dept = "HR";
This.manager = "John Johnson";
}
function Printprop () {
var ken = new Employee ();
For (in Ken) {
Alert (property);
}
}
As we mentioned earlier, all internal objects and custom objects are inherited from object objects and are superclass objects of all objects. You can create an instance of an object. As follows:
var myObject = new Object ();
Objects of type object have many properties and methods, and you can view the relevant manuals. It just defines one of the simplest empty objects, and you can also pass parameters to the object constructor, which returns an instantiated object of the corresponding type value. Remember, the type of the return value is some type of object (such as String, number, or object). This approach differs from the direct use of assignment strings or numeric constants, mainly in terms of type. As shown below:
var myObject = new Object ("foo"); Return value type is Object
var myObject = new String ("foo"); The return value type is object, and the effect is the same
And
var myObject = "Foo"; Return value type is string
You can see the subtle difference in the debugger's type column, which is the difference between a simple type and an object type. However, you do not see these internal differences through alert invocation, because all object type values are automatically invoked by the ToString method for string type conversions during alert invocation, and the conversion rules are described in the JavaScript manual. If you are alert to a custom object and it does not define the ToString method, its return value will be "[Object]". For the Math object, when you look at its Math.constructor property, you get a different content from the other internal object, "function object () ..." Object constructor, which returns "function function () ..." with other objects. The constructors are very different. The reason is simple, because the Math object cannot be created with the new operator.
In addition, if the value in the incoming object constructor is an object, it returns the object intact. Remember, this action is only a reference, not a copy.
Request object's Properties
In the previous example code, an example of enumerating object properties in a circular fashion has been seen. In fact, through the for...in statements, whether it is any object and array, the elements under it, attributes and methods can be traversed. Examples are as follows:
function Employee () {
this.dept = "HR";
This.manager = "John Johnson";
}
function Printprop () {
var ken = new Employee ();
For (in Ken) {
Alert (property + ":" + Ken[property]);
}
}
In the traversal test, you will find that the custom object and host object can be used to enumerate the attributes under it, and for internal objects, there are few attributes to traverse out, why say almost? Because the JavaScript engine is different for the Mozilla kernel browsing and IE kernel browsers, Mozilla can enumerate some of the content, while the principle of enumeration is unknown.
For each object, you can also use the hasOwnProperty method to detect whether it has a property or method. Because hasOwnProperty is a method under Object objects, all objects have this method. However, it is important to note that this method can only detect members defined through the This keyword, and if a member is defined through a prototype chain, this method returns FALSE. That is, the properties and methods inherited by prototype, and the properties and methods defined by prototype, cannot be detected through hasOwnProperty. From this, we can see that the properties and methods defined by this keyword are in the same address space as the object itself; And the properties and methods defined by prototype, is managed through the so-called "prototype chain", where the properties and methods are not located between the same address space, and when they invoke such a property or method, they must pass a "linked list" to index to a property or method under it. It is also said that invoking properties and methods that are defined in a prototype way will have a "backtracking" action similar to the linked list.
Like hasOwnProperty, for each property in an object, we can also test whether it can be enumerated by propertyisenumerable. As shown below:
function Employee1 () {
this.dept = "HR";
This.manager = "John Johnson";
This.month = new Array ("Feb", "N", "mar");
}
var Ken = new Employee1 ();
Ken.month.propertyIsEnumerable (0);
We can see that its syntax is propertyisenumerable followed by the element index of the array or the name of the property in the object. Similarly, for a property or method in a prototype chain, it is not considered, and the result is, of course, returning false.
For JavaScript and Java comparisons
Unlike Java, a class-based language, JavaScript is a prototype based language. This trait affects every aspect. For example, the term instance has a special meaning in a class-based language, it means that an instance is an independent individual belonging to a particular class, and is a true realization of the class definition; In JavaScript, the term instance does not have this meaning, because in its syntax, the class and the instance are indistinguishable. Although, an instance can be used to indicate that an object was generated using a particular constructor. As shown below:
Function Superclass () {
This.bye = Superbye;
This.hello = Superhello;
}
function Subclass () {
This.bye = Subbye;
}
Subclass.prototype = new Superclass;
function Superhello () {
Return "Hello from superclass";
}
function Superbye () {
Return to "Bye from superclass";
}
function Subbye () {
Return to "Bye from subclass";
}
var newclass = new Subclass ();
Newclass is an instance of subclass, which is generated by the subclass constructor. And if you use a class-based language, as shown below is the Java equivalent implementation.
public class Superclass {
Public superclass () {
This.bye = Superbye;
This.hello = Superhello;
}
}
public class subclass extends superclass {
Public Subclass () {
This.bye = Subbye;
}
}
Conclusion
In the above sections, we describe in detail the object-oriented implementations in Javascipt, or only the simulation implementations. In the meantime, I showed you how to implement it. It also explains how to detect relationships between objects, how to print properties and test a particular property, and a simple comparison between JavaScript and Java. But this is obviously not enough, so JavaScript object-oriented programming is very diverse, and the format is very complex. I intend to summarize the question of the format of the questions in the later section, highlighting content and implementation related to the object approach, as well as the prototype chain (prototype chain) problem mentioned above.