Shallow understanding

JavaScript has no concept of classes, so object creation differs from object-oriented languages.

JS is a language based on objects, the concept of objects in JS system is very important, so it is necessary to clearly understand the common methods of object creation in JS and their limitations

Create objects using Object or Object literals

Before we talk about creating objects in factory mode, let’s review the most basic methods of creating objects in JS. For example, what if I want to create a student object? Most simply, new an Object:

var student = new Object();
student.name = "easy";
student.age = "20";

Copy the code

The student object is created with two attributes, name and age, assigned to “easy” and 20, respectively.

If this approach feels too encapsulated, we can also create student objects using object literals:

var sutdent = {
  name : "easy",
  age : 20
};

Copy the code

It looks perfect. But there is a very sharp problem: when we create the same class student1, student2… Studentn, we have to repeat the above code n times.

var sutdent1 = { name : "easy1", age : 20 }; var sutdent2 = { name : "easy2", age : 20 }; . var sutdentn = { name : "easyn", age : 20 };Copy the code

The constructor pattern creates objects

We used the constructor when we created a native Object like Object above:

var obj = new Object();

Copy the code

We have also used its constructor when creating objects of type Array:

var arr = new Array(10); // Construct an array object of initial length 10Copy the code

Before creating objects using custom constructors, let’s first look at the differences between constructors and normal functions.

For one thing, there is really no special syntax for creating constructors; the only difference is that they call methods. For any function called with the new operator, it is a constructor; If it is not called with the new operator, it is a normal function.

Second, by convention, constructor names start with a capital letter and ordinary functions start with a lowercase letter, which makes it easier to explicitly distinguish between the two. For example, new Array(), new Object() above.

Third, when a constructor is called using the new operator, it experiences (1) the creation of a new object; (2) Assign the constructor scope to the new object (make this refer to the new object); (3) Execute constructor code; (4) Return a new object; Four stages.

Now that we know the difference between constructors and normal functions, we use constructors to rewrite the factory mode function and add a method attribute:

function Student(name, age) {
  this.name = name;
  this.age = age;
  this.alertName = function(){
    alert(this.name)
  };
}

function Fruit(name, color) {
  this.name = name;
  this.color = color;
  this.alertName = function(){
    alert(this.name)
  };
}

Copy the code

Create Student and Fruit objects, respectively:

var v1 = new Student("easy", 20);
var v2 = new Fruit("apple", "green");

Copy the code

The instanceof operator can be used to distinguish Student from Fruit:

alert(v1 instanceof Student); //true alert(v2 instanceof Student); //false alert(v1 instanceof Fruit); //false alert(v2 instanceof Fruit); //true alert(v1 instanceof Object); //true Any Object inherits from Object alert(v2 instanceof Object); //true Any Object inherits from ObjectCopy the code

This eliminates the awkwardness of factory mode’s inability to distinguish between object types. So is using constructors to create objects perfect?

We know that in JS, functions are objects. So, when we instantiate more than one Student object:

var v1 = new Student("easy1", 20); var v2 = new Student("easy2", 20); . var vn = new Student("easyn", 20);Copy the code

Where the common alertName() function is instantiated n times, we can check that different Student objects do not share the alertName() function:

alert(v1.alertName == v2.alertName);  //flase

Copy the code

This is definitely a waste of memory. As we know, this object is bound at run time based on the execution environment of the function. In global functions, this object is equivalent to window; In object methods, this refers to the object. In the constructor above:

this.alertName = function(){
    alert(this.name)
  };

Copy the code

We bind the alertName() function to the object when it is created (before the alertName function is executed). We can do this again when executing the function by moving the object method outside the constructor:

function Student(name, age) {
  this.name = name;
  this.age = age;
  this.alertName = alertName;
}

function alertName() {
  alert(this.name);
}

var stu1 = new Student("easy1", 20);
var stu2 = new Student("easy2", 20);

Copy the code

This object is bound to STU1 only when stu1.alert() is called.

We define the alertName() function as a global function, so that the alertName property in the object is set as a pointer to the global function. Thus, stu1 and STU2 share the global function, which solves the memory waste problem.

However, using global functions to solve the problem of internal object sharing is not a good solution after all. If there are too many global functions defined in this way, our intention to encapsulate custom objects becomes almost impossible. A better solution is to use the prototype object pattern.

Deep understanding of

New expressions are used in conjunction with constructors, such as new String(” a String “), which calls the built-in String function to construct a String object. Create an object that does the same thing by defining the constructor and then calling the new expression:

Function CO(){this. p = "I'm in a constructed object"; this .alertP = function(){ alert( this .p); } } var o2 = newCO();Copy the code

So what happens when you use the new operator to call a constructor? It was very simple. Four things happened:

var obj  ={};
obj.__proto__ = CO.prototype;
CO.call(obj);
return  obj;
Copy the code

In the first line, create an empty object obj.

The second line points the empty object’s __proto__ member to the constructor object’s Prototype member, which is the most critical step, described in more detail below.

In line 3, we assign the constructor’s scope to the new object, so this in the CA function points to the new object obj, and then call CO. So we assign a member variable P to obj, and the value of the member variable is “I ‘min constructed object”.

Line 4, return the new object obj. This is especially the case when a constructor contains a return statement, as described below.

Define JavaScript constructors correctly

Unlike other mainstream programming languages, JavaScript’s constructor does not exist as a specific method of a class; When any ordinary function is used to create a class of objects, it is called a constructor, or constructor. To be a true constructor, a function must satisfy the following conditions:

Set the properties of the new object (this) inside the function, usually by adding properties and methods.

2. Constructors can contain return statements (not recommended), but the return value must be this or some other non-object type.

The constructor CO defined above is a standard, simple constructor. The following example defines a function C1 that returns an object. We can call it using a new expression that correctly returns an object:

Function C1(){var o = {p: "I'm in C1"} return o; } var o1 = new C1(); alert(o1.p); / / I 'm p in C1Copy the code

However, this approach is not recommended because the prototype of Object O1 is the prototype of the Object O defined inside function C1 (object.prototype). This is equivalent to executing the first three steps of a normal new expression and returning the value of C1 in the fourth step. This approach is also inconvenient to create a large number of objects of the same type, is not conducive to the use of advanced features such as inheritance, and is prone to confusion and should be abandoned.

It is a measure of JavaScript’s flexibility that a constructor can be used as a normal function in some cases. The following example defines C2 as a “multi-purpose” function:

function C2(a, b){ this .p = a + b; this .alertP = function(){ alert( this .p); } return this .p; Var C2 = new C2(2, 3); var C2 = new C2(2, 3); c2.alertP(); // result is 5 alert(C2(2, 3)); // The result is 5Copy the code

This function can be used either as a constructor to construct an object or as a normal function. When used as a normal function, it takes two arguments and returns the sum of the two. For code readability and maintainability, it is recommended that functions that are constructors be unadulterated with code other than constructors; Similarly, generic function functions should not be used as constructors.

Why use constructors

From the above definition, on the surface, constructors seem to simply initialize a newly created object, adding member variables and methods; However, constructors do much more than that. To illustrate the significance of using constructors, let’s review the previous example. Var o2 = new CO(); When you create an object, four things happen:

var obj  ={};
obj.__proto__ = CO.prototype;
CO.call(obj);
return  obj;
Copy the code

Let’s say the most important thing is the second step, which assigns the __prop__ property of the newly generated object to the constructor’s Prototype property so that all objects created through the constructor can share the same prototype. This means that all objects created by the same constructor inherit from the same object, so they are all objects of the same class. The details of prototype and inheritance will be covered in another article.

The __prop__ attribute does not exist in the JavaScript standard, but it is now a standard attribute by default in some mainstream JavaScript execution environments to point to constructor stereotypes. This property is not visible by default, and the details of implementation vary from execution environment to execution environment. For example, Internet Explorer does not have this property. All we need to know is that there is a pointer to the constructor prototype inside the JavaScript object, that the pointer is automatically assigned when a new expression is called, and that we should not modify it.

In the four steps of constructing an object, we can see that, except for the second step, we do not need to use new expressions to achieve other steps, so new expressions are not only a simplification of these four steps, but also the only way to achieve inheritance.

Confusion !!!!!!!!!

One area of confusion about JavaScript constructors is the stereotype’s constructor property. In JavaScript, every function has the default prototype object property, which by default contains two member properties: constructor and __proto__. The details of the stereotype are beyond this article; we are now concerned with the constructor property.

In object-oriented thinking, we say that a constructor is equivalent to the definition of a “class,” so it would be a mistake to think that the constructor property is the actual constructor of the class, and that when a new expression creates an object, the constructor is called directly to initialize the object. The actual process of new expression execution was described above (four steps), of which the third step is used to initialize the object, and the initialization function called is the “class function” itself, not constructor. This may not be easy to understand without considering the question, but let’s give an example to illustrate:

function C3(a, b){ this .p = a + b; this .alertP = function(){ alert( this .p); Function fake(){this.p = 100;}} // We define a function to override the constructor of the C3 prototype, attempting to change the value of the attribute p. } C3.prototype.constructor = fake; Constructor var C3 = new C3(2, 3); c3.alertP(); // The result is still 5Copy the code

The above code manually changes the constructor function in the C3 prototype, but it has no real effect on the creation of the C3 object, as you can see in the new expression, only the constructor itself initializes the object. So what does the constructor property do? In general, we can use the constructor attribute to test the type of an object:

var myArray = [ 1 , 2 , 3 ];
(myArray.constructor == Array);  // true
Copy the code

This works for simple objects, but not for complex situations such as inheritance or cross-windows:

function f() { this .foo = 1 ; } function s() { this .bar = 2 ; } s.prototype = new f(); Var son = new s(); Constructor == s; // Create a subclass object using the constructor (son.constructor == s); // false (son.constructor == f); // trueCopy the code

Such results may not be what you expect, so be careful when using the constructor attribute, or avoid it altogether. — — — — — — — — copyright statement: this article to CSDN blogger jxjy “Easy – Sir” of the original article