But it should be noted that each object created by using the constructor to operate this object not only has its own member data, but also has its own method data. In other words, the code body of the method (data that embodies the function logic) has a copy in each object. Although the logic of each copy of code is the same, the objects do save a copy of the code. The last sentence in the above example illustrates this fact, which also explains the concept that functions in JavaScript are objects.
It is obviously a waste to have a method code for objects of the same type. In traditional object languages, method functions are not an object concept like JavaScript. Even if there are variations like function pointers, method pointers or delegates, their essence is also a reference to the same code. This is difficult to encounter in general object languages.
However, the JavaScript language has great flexibility. We can first define a unique method function body and use this unique function object as its method when constructing this object, and then we can share the method logic. For example:
[Ctrl+A Select all Note:Introducing external Js requires refreshing the page before execution]
Among them, the output result of the last line shows that the two objects do share a function object. Although this program achieves the purpose of sharing a copy of method code, it is not very elegant. Because when defining the SayHello method, it cannot reflect its relationship with the Person class. The word "elegance" is used to describe code, and I don't know who proposed it first. However, this word reflects that programmers have developed from the perspective of pursuing the accuracy, efficiency, reliability and easy-to-read code to the level of pursuing the aesthetic feeling of code and artistic realm, and program life has become more romantic.
Obviously, JavaScript has long thought of this problem, and her designers have provided an interesting prototype concept for this.
First look at the prototype
prototype originated from French, and the standard translation of the software industry is "prototype", which represents the initial form of things and also contains the meaning of models and templates. The concept of prototype in JavaScript aptly reflects the meaning of the word, and we cannot understand it as the pre-declared concept of C++'s prototype.
All JavaScript's function type objects have a prototype property. This prototype property itself is an object of type object, so we can also add any properties and methods to this prototype object. Since prototype is the "prototype" of an object, the objects constructed by this function should have the characteristics of this "prototype". In fact, all properties and methods defined on the constructor's prototype can be directly accessed and called through the objects they construct. It can also be said that prototype provides a mechanism for a group of similar objects to share attributes and methods.
Let’s first look at the following code:
[Ctrl+A Select all Note:Introducing external Js requires refreshing the page before execution]
The results of the program running show that the methods defined on the prototype of the constructor can indeed be called directly through the object, and the code is shared. Obviously, the way to set the method to prototype seems much more elegant. Although the call form has not changed, it logically reflects the relationship between methods and classes. Compared with the previous way of writing, it is easier to understand and organize the code.
So, what about constructors with multi-level types?
Let’s look at the following code:
Copy the codeThe code is as follows:
1 function Person(name) //Basic class constructor
2 {
3 = name;
4 };
5
6 = function() //Add method to the prototype of the base class constructor
7 {
8 alert("Hello, I'm " + );
9 };
10
11 function Employee(name, salary) //Subclass constructor
12 {
13 (this, name); //Calling the base class constructor
14 = salary;
15 };
16
17 = new Person(); //Build a base class object as a prototype of the subclass prototype, it's very interesting here.
18
19 = function() //Add method to add constructor to subclasses
20 {
21 alert( + " $" + );
22 };
23
24 var BillGates = new Person("Bill Gates"); //Create a BillGates object with the base class Person
25 var SteveJobs = new Employee("Steve Jobs", 1234); //Create the SteveJobs object of the subclass Employee
26
27 (); //The method of directly calling to prototype through the object
28 (); //Directly call the base class prototype method through the subclass object, follow!
29 (); //Directly call the method of the subclass prototype through the subclass object
30
31 alert( == ); //Show: true, indicating that the method of prototype is shared
It turns out that in JavaScript, prototype not only allows objects to share their wealth, but also prototype has the nature of seeking roots and asking ancestors, so that the legacy of ancestors can be passed down from generation to generation. When reading attributes or calling methods from an object, if the object itself does not have such attributes or methods, it will search for them in the prototype object you are associated with; if there is no prototype, it will search for them in the senior prototype you are associated with until the search or traceability process is over.
In JavaScript, the object's attribute and method traceability mechanism is implemented through the so-called prototype chain. When constructing an object with the new operator, the constructor's prototype object will also be assigned to the newly created object, becoming the built-in prototype object of the object. The built-in prototype object of the object should be invisible to the outside world. Although some browsers (such as Firefox) can allow us to access this built-in prototype object, this is not recommended. The built-in prototype object itself is an object, and it also has its own associated prototype object, thus forming the so-called prototype chain.
At the end of the prototype chain, it is the prototype object pointed to by the prototype attribute of the Object constructor. This prototype object is the oldest ancestor of all objects, and this ancestor implemented methods that all objects such as toString should have innately. Other built-in constructors, such as Function, Boolean, String, Date and RegExp, are inherited from this ancestor, but they each define their own attributes and methods, so that their descendants show the characteristics of their respective clans.
Isn’t this “inheritance”? Yes, this is "inheritance", which is the "prototype inheritance" unique to JavaScript.
"Prototype inheritance" is kind and harsh. The prototype object selflessly contributes its attributes and methods to children, and does not force children to follow them. It allows some naughty children to act independently according to their interests and hobbies. From this point of view, the prototype object is a kind mother. However, although any child can do his own thing, he cannot move the existing property of the prototype object, because it may affect the interests of other children. From this point of view, the prototype object is like a strict father. Let’s look at the following code to understand this meaning:
Copy the codeThe code is as follows:
function Person(name)
{
= name;
};
= "Microsoft"; //Prototype properties
= function()//Prototype method
{
alert("Hello, I'm " + + " of " + );
};
var BillGates = new Person("Bill Gates");
(); //Because it inherits the prototype, the output is regular: Hello, I'm Bill Gates
var SteveJobs = new Person("Steve Jobs");
= "Apple"; //Set your own company attribute to cover up the company attribute of the prototype.
= function() //Implement your own SayHello method to cover up the prototype SayHello method
{
alert("Hi, " + + " like " + + ", ha ha ha ");
};
(); //These are all attributes and methods that you cover themselves, output: Hi, Steve Jobs like Apple, ha ha ha
(); //SteveJobs' coverage does not affect the prototype object, BillGates still outputs as usual
However, what is more magical than static object language is that we can dynamically add new properties and methods to prototype objects at any time, thereby dynamically extending the functional characteristics of the base class. This is hard to imagine in static object languages. Let's look at the following code:
Copy the codeThe code is as follows:
function Person(name)
{
= name;
};
= function()//Methods defined before creating an object
{
alert("Hello, I'm " + );
};
var BillGates = new Person("Bill Gates"); //Create an object
();
= function()// Method of dynamically expanding the prototype after creating an object
{
alert("Poor " + + ", bye bye!");
};
();//The dynamically expanded method can be called immediately by previously established objects.
Prototype extension
I believe you have a very high understanding. You may think like this: If you add some new methods and attributes to the prototype of functions such as Object and Function built into JavaScript, can you extend the functions of JavaScript?
Then, congratulations, you got it!
Today, with the rapid development of AJAX technology, many successful JavaScript runners of AJAX projects have greatly expanded the prototype function of built-in functions. For example, Microsoft's AJAX has added a lot of new features to these built-in functions and their prototypes, thereby enhancing the functions of JavaScript.
Let's look at a code excerpted from:
Copy the codeThe code is as follows:
= function String$trim() {
if ( !== 0) throw ();
return (/^\s+|\s+$/g, '');
}
This code extends a trim method to the prototype of the built-in String function, so all String class objects have trim methods. With this extension, you will need to remove the blanks in the two segments of the string in the future, so you don’t need to process them separately, because any string has this extension function, just call it, which is really convenient.
Of course, almost few people add methods to the Object's prototype, because that will affect all objects unless this method is indeed required for all objects in your architecture.
Two years ago, Microsoft used a technology called "closure" to simulate "classes" in the early days of designing the AJAX class library. The rough model is as follows:
Copy the codeThe code is as follows:
function Person(firstName, lastName, age)
{
//Private variables:
var _firstName = firstName;
var _lastName = lastName;
//Public variables:
= age;
//method:
= function()
{
return(firstName + " " + lastName);
};
= function()
{
alert("Hello, I'm " + firstName + " " + lastName);
};
};
var BillGates = new Person("Bill", "Gates", 53);
var SteveJobs = new Person("Steve", "Jobs", 53);
();
();
alert(() + " " + );
alert(); //Private variables cannot be accessed here
Obviously, the class description of this model is particularly like the description form of C# language. It defines private members, public attributes and available methods in sequence in a constructor, which seems very elegant. In particular, the "closure" mechanism can simulate the protection mechanism for private members and do a very beautiful job.
The so-called "closure" is to define another function as the method function in the constructor body, and the method function of this object in turn refers to temporary variables in the outer outer function body. This allows the temporary variable value used by the original constructor body to be indirectly maintained as long as the target object can always maintain its method during its lifetime. Although the initial constructor call has ended and the names of the temporary variables have disappeared, the value of the variable can always be referenced within the target object's method, and the value can only be accessed in this method. Even if the same constructor is called again, only new objects and methods will be generated. The new temporary variable only corresponds to the new value, and is independent of the last call. It's really clever!
But we have said before that setting up a method for each object is a big waste. Also, the mechanism of "closure" that indirectly maintains variable values often creates problems for JavaScript's garbage collector. Especially when encountering complex circular references between objects, the judgment logic of garbage collection is very complicated. Coincidentally, early versions of IE browser did have memory leaks in JavaScript garbage collection. Coupled with the poor performance of the "closure" model in performance testing, Microsoft eventually abandoned the "closure" model and switched to the "prototype" model. As the saying goes, "There is gain and loss."
The prototype model requires a constructor to define the members of the object, but the method is attached to the prototype of the constructor. The general writing is as follows:
Copy the codeThe code is as follows:
//Define the constructor
function Person(name)
{
= name; //Define members in the constructor
};
//The method is defined on the constructor's prototype
= function()
{
alert("Hello, I'm " + );
};
//Subclass constructor
function Employee(name, salary)
{
(this, name); //Calling the upper constructor
= salary; //Extended members
};
//Subclass constructor first needs to use the upper-level constructor to establish a prototype object to realize the concept of inheritance.
= new Person()//Only the method of its prototype is needed, the members of this object have no meaning!
//Subclass methods are also defined on the constructor
= function()
{
alert( + " $" + );
};
var BillGates = new Person("Bill Gates");
();
var SteveJobs = new Employee("Steve Jobs", 1234);
();
();
In the prototype model, in order to implement class inheritance, the prototype of the subclass constructor must first be set to an object instance of the parent class. The purpose of creating this parent object instance is to form a prototype chain to share the upper prototype method. However, when creating this instance object, the upper constructor will also set object members for it, which are meaningless for inheritance. Although we did not pass parameters to the constructor, we did create several useless members, although their values were undefined, which is also a waste.
well! There is no perfect thing in the world!
The true meaning of the prototype
Just as we were deeply moved, a red light flashed in the sky, and Guanyin Bodhisattva appeared in the auspicious clouds. I saw her holding a jade bottle in her hand, gently brushing the green willow branches, sprinkling a few drops of nectar, which immediately added new aura to JavaScript.
The nectar sprinkled by Guanyin condensed into pieces in the world of JavaScript and became something called "grammatical nectar". This syntax nectar can make the code we write look more like an object language.
If you want to know what this "grammatical nectar" is, please listen carefully.
Before understanding these syntactic nectars, we need to review the process of JavaScript constructing objects.
We already know that the process of creating an object in the form of var anObject = new aFunction() can actually be divided into three steps: the first step is to create a new object; the second step is to set the built-in prototype object of the object to the prototype object referenced by the constructor prototype; the third step is to call the constructor as this parameter to complete the initialization work such as member settings. After the object is established, any access and operation on the object is only related to the object itself and the series of objects on the prototype chain, and has no longer anything to do with the constructor. In other words, the constructor only introduces the prototype object and initializes the object when creating the object.
So, can we define an object ourselves as a prototype, describe the class on this prototype, and then set the prototype to the newly created object and treat it as the object's class? Can we use a method in this prototype as a constructor to initialize the newly created object? For example, we define such a prototype object:
Copy the codeThe code is as follows:
var Person =//Define an object as a prototype class
{
Create: function(name, age) //This is the constructor
{
= name;
= age;
},
SayHello: function()//Define method
{
alert("Hello, I'm " + );
},
HowOld: function()//Define method
{
alert( + " is " + + " years old.");
}
};
But unfortunately, we can hardly access the built-in prototype properties of the object! Although some browsers can access the built-in prototype of the object, doing so can only limit which browser the user must use. This is almost impossible, either.
So, can we use a function object as a medium, use the prototype attribute of the function object to redirect the prototype and pass it to the newly created object with the new operator?
In fact, code like this can achieve this goal:
function anyfunc(){}; //Define a function shell
= Person; //Put the prototype object to the transit site prototype
var BillGates = new anyfunc(); //The built-in prototype of the new object will be the prototype object we expect
However, this anyfunc function is just a body, and after using this body, it becomes a redundant thing, and this is no different from directly using constructors to create objects, which is a bit unhappy.
However, if we write these codes into a general function, and the function body becomes a function within the function, can not this internal function automatically disappear after the outer function exits the scope? Moreover, we can use the prototype object as a parameter to the general function, and let the general function return the created object. What we need is the following form:
Copy the codeThe code is as follows:
function New(aClass, aParams) //Universal creation function
{
function new_() //Define temporary transit function shell
{
(this, aParams); //Calling the constructor defined in the prototype, redirecting the construction logic and constructing parameters
};
new_.prototype= aClass; //Prepare to transfer the prototype object
return new new_(); �
};
var Person = //Defined Class
{
Create: function(name, age)
{
= name;
= age;
},
SayHello: function()
{
alert("Hello, I'm " + );
},
HowOld: function()
{
alert( + " is " + + " years old.");
}
};
var BillGates = New(Person, ["Bill Gates", 53]); //Calling a general function to create an object and passing the constructor parameters in an array.
();
();
alert( == Object); //Output: true
The general function New() here is a "grammatical nectar"! This syntax nectar not only redirects the prototype object, but also redirects the constructor logic and constructor parameters.
Interestingly, every time the object is created and exits the scope of the New function, the temporary new_function object will be automatically released. Since the prototype property of new_ is set to the new prototype object, the reference chain has been untie the original prototype object and new_, and the temporary functions and their original prototype objects will be correctly recycled. The last sentence of the above code proves that the constructor property of the newly created object returns the Object function. In fact, the newly created object itself and its prototype do not have the constructor attribute, so what is returned is the constructor function of the top-level prototype object, namely the Object.
With the syntax nectar of New, the definition of the class is very similar to the static object languages of C#. How quiet and elegant such code looks!
Of course, this code only shows the concept of "grammatical nectar". We also need more syntactic nectar to achieve the writing of class levels and their inheritance relationships with concise and elegant code. OK, let's take a look at a richer example:
Copy the codeThe code is as follows:
//Grammar nectar:
var object = //Define lowercase object basic class to implement the most basic methods, etc.
{
isA: function(aType)//A basic method for judging the relationship between classes and objects and between classes
{
var self = this;
while(self)
{
if (self == aType)
return true;
self = ;
};
return false;
}
};
function Class(aBaseClass, aClassDefine) //Create a class function to declare the class and inheritance relationship
{
function class_() //Create a temporary function shell of the class
{
= aBaseClass; //We agree to each class with a Type property and reference its inherited class.
for(var member in aClassDefine)
this[member]= aClassDefine[member]; //Copy all definitions of the class to the currently created class
};
class_.prototype = aBaseClass;
return new class_();
};
function New(aClass, aParams) //A function to create an object, used for object creation of any class
{
function new_() // Create the temporary function shell of the object
{
= aClass; //We also agree a Type property for each object, and we can access the class to which the object belongs.
if ()
(this, aParams); //We agree that the constructors of all classes are called Create, which is similar to DELPHI.
};
new_.prototype = aClass;
return new new_();
};
//The application effect of grammatical nectar:
var Person = Class(object, //Derived to object basic class
{
Create: function(name, age)
{
= name;
= age;
},
SayHello: function()
{
alert("Hello, I'm " + + ", " + + " years old.");
}
});
var Employee = Class(Person, //Is it very similar to the general object language?
{
Create: function(name, age, salary)
{
(this, name, age); //Calling the constructor of the base class
= salary;
},
ShowMeTheMoney: function()
{
alert( + " $" + );
}
});
var BillGates = New(Person, ["Bill Gates", 53]);
var SteveJobs = New(Employee, ["Steve Jobs", 53, 1234]);
();
();
();
var LittleBill = New(, ["Little Bill", 6]); //Create LittleBill according to the type of BillGate
();
alert((Person)); //true
alert((Employee)); //false
alert((Person)); //true
alert((Employee)); //false
alert((Person)); //true
There is no need to do too much "grammatical nectar". Just a little bit can improve the readability and fluency of the entire code, so that the code appears more elegant. With these syntax nectars, JavaScript is very similar to a general object language, and it feels much more enjoyable when writing code!
Happily, JavaScript programs nourished by these nectars will be more efficient. Because its prototype object does not have useless object-level members, and there is no constructor attribute body, and there is less involvement with the constructor function, but the sharing of the method is still maintained. This allows JavaScript to spend a lot of less time tracing prototype chains and searching for attributes and methods.
Let’s call this form the “Nectar Model”! In fact, the prototype usage of this "Nanlu Model" is in line with the original meaning of the concept of prototype and the true meaning of the JavaScript prototype!
I believe that when Microsoft engineers designed the AJAX architecture saw this nectar model, they would regret not moving the AJAX department from the United States to the Guanyin Temple in China earlier, and missed the enlightenment of Guanyin Bodhisattva. Of course, we can only play with Bill Gates as an object in the code example. It will definitely not be easy to let him give up God and convert to me. The opportunity has not come! If one day you see this nectar model in Microsoft's new AJAX library, that's the real fate!
The joy of programming
Today, with the rapid development of the software industry, various programming languages are emerging one after another. The birth of new languages and the evolution of old languages seem to have dazzled us. In order to adapt to the trend of object-oriented programming, the JavaScript language is also developing towards a completely object-oriented direction. The new JavaScript standard has semantically expanded many new object-oriented elements. On the contrary, many static object languages are also moving towards the concise and elegant direction of JavaScript. For example, the new version of C# language absorbs concise notation like JSON, as well as some other forms of JavaScript features.
We should see that with the development and popularization of RIA (strongly connected applications), AJAX technology will gradually fade out of the world, and JavaScript will eventually disappear or evolve into other forms of language. But no matter how programming languages develop and evolve, the programming world will always maintain infinite vitality in the inseparable entanglement of "data" and "code". As long as we can see through this, we can easily learn and understand all kinds of new things in the software world. Whether it is familiar procedural programming, developing functional programming, and future large-scale parallel programming of quantum entangled states, we have enough mana to solve all complex problems.
The Buddha finally said lightly: As long as we let go of those superficial "categories" and those objects' "self", we can reach a state of "the object is originally rootless and the type is invisible", thereby integrating the self into the cycle of life in the entire universe. We will have no self and no selfish desires. You are me, I am you, you have me, I have you. At this time, when we look at this vibrant programming world, our hearts will naturally develop infinite love, which is not hypocritical but sincere. Caring for others is caring for yourself, and caring for everything in this world. Then, our hearts are always happy, our programs are always happy, our classes are always happy, and our objects are always happy. This is the bliss of programming!
Speaking of this, all the monks here felt enlightened and suddenly felt enlightened. Looking at the one on the left, I was already overjoyed, and looking at the one on the right, I was also overjoyed.
When I looked back suddenly, I only saw you smiling with a flower...
Original work: Li Zhan (leadzen). Shenzhen 2008-2-23
Package file download
Previous page12Read the full text