Javascript Object-Oriented Programming

Keywords: Javascript Attribute Programming ECMAScript

Recently, because a primary school students to make learning plans, so also record some knowledge points, easy for the students behind the learning exchange. This article is about object-oriented programming of Javascript. It is mainly introduced in three aspects: 1. Understanding object properties; 2. Understanding and creating objects; 3. Understanding inheritance.

I. Understanding Object Attributes
First, let's understand what Javascript objects are. In Javascript, everything is an object. The simplest way to create a custom object is to create an instance of Object, as follows:

var person = new Object();
person.age = 29;

// The literal form of the object:
var person = {
     age: 29
};

ECMAScript has two attributes: data attributes and accessor attributes.

Data attributes:
Data attributes have four characteristics that describe their behavior:
Configurable: Represents the ability to redefine attributes by deleting them all.
Enumerable: Indicates whether properties can be returned through a for in loop.
Writable: Indicates whether the value of an attribute can be modified.
Value: Data value containing this attribute.
To modify the default configuration of attributes, you must use Object.defineProperty(), which takes three parameters: the object in which the attribute is located, the name of the attribute, and a descriptive object.
For example:

var person = {};
Object.defineProperty(persion, 'name', {
      writable: false,
      value: 'Nicholas'
});

alert(person.name); //Nicholas
person.name = 'Greg';
alert(person.name); //Nicholas

Accessor properties:
Accessor properties contain a pair of setter and getter functions. It includes the following four features:
Configurable: Can it be redefined by delete delete attributes? Default value:true
Enumerable: Can it be enumerated for-in? Default value:true
Get: Read property values. Default value: undefined
Set: Writes property values. Default value: undefined

var dog = {
    _age: 2,
    weight: 10
}

Object.defineProperty(dog, 'age', {
    get: function () {
        return this._age
    },
    set: function (newVal) {
        this._age = newVal
        this.weight += 1
    }
})

Knowing the attributes of an object, how do we create it?

2. The Way to Create Objects

There are usually several ways to create objects:
1. Factory Model
Let's take an example:

function createPerson(name, age, job) {
    var o = new object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        alert(this.name);
   };
   Return o;
}

var person = createPerson('Greg', 27, 'Doctor');

Although the factory pattern solves the problem of creating multiple similar objects, it does not solve the problem of identifying them (that is, how to know the type of an object).

2. Constructor pattern
Let's take an example:
**function Person(name, age, job) {

this.name = name;
this.age = age;
this.job = job;
this.sayName =  function () {
    alert(this.name);

};
}
var person = new Person('Greg', 27, 'Doctor');**

Constructors should always start with a capital letter, while non-constructors should start with a lowercase letter. One of the attributes to be mentioned here is Constructor. Each new instance has a Constructor (constructor) attribute, which points to the constructor.
The Constructor attribute of an object was originally used to identify the object type. However, when it comes to detecting object types, the instanceof operator is more reliable.
alert(person1 instanceof object); //true

The problem with constructors is that each method has to be recreated on each instance. Of course, the definition of a function can be transferred to the outside of the constructor to solve this problem. The following examples are given:

**function Person(name, age, job) {

this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;

}
function sayName() {

alert(this.name);

}
var person = new Person('Greg', 27, 'Doctor');**

The problem here is that functions defined in global scope can actually be called by only one object, which makes global scope a little misnomer. What is even more unacceptable is that if exclusivity requires defining many methods, then it requires defining many global functions. Now let's look at the prototype pattern, which can't solve this problem.

3. Prototype Model
Prototype is the prototype object of the object instance created by calling the constructor. Let's take the following example to understand this sentence:

function Person() {
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); // "Nicholas"

Unlike the constructor pattern, these attributes and methods of the new object are shared by all instances.
In view of this characteristic, we should pay attention to several points.
First, if the attributes and methods of an instance have the same name as those of a prototype, who has the highest priority?
Of course, the attributes and methods of the created instances are of high priority.
Secondly, will the modification of the attributes and methods of the instance affect the attributes and methods of the same name of the prototype?
Can't
Here we mention hasOwnProperty(), which uses the hasOwnProperty() method to detect whether an attribute exists in an instance or in a prototype.
Thirdly, how to judge whether an attribute exists in the prototype?

function hasPrototypeProperty(object, name) {
    return !Object.hasOwnProperty(name) && (name in object);
}

hasOwnProperty() returns true only if the attribute exists in the instance, so as long as the in operator returns true and hasOwnProperty() returns false, it can be determined that the attribute is the attribute in the prototype. In this case, Constructor. Every time a function is created, its Prototype object is created, and the object automatically obtains the Constructor attribute.
Let's take an example:

function Person() {

}

Person.prototype = {
    constructor: Person,
    name: "Nichloas",
    age: 29,
    job: "Software Engineer",
    sayName: function () {
         alert(this.name);
    }  
};

The above code specifically contains a Constructor property and sets its value to Person, thus ensuring that the appropriate value can be accessed through this property. However, resetting the Constructor property in this way will cause its Enumerable property to be set to true, and the native Constructor property is not enumerated by default.

Object.defineProperty(Person.prototype, "Constructor", {
    enumerable: false,
    value: Person
});

The problem of prototype objects:
First, it omits the step of passing initialization parameters to constructors.
Then, all the attributes in the prototype are shared by many instances. For attributes containing reference type values, the problem is more prominent. So, constructors and prototypes are used the most frequently

4. Combining constructor pattern with prototype pattern

function Person(name, age, job) {
     this.name = name;
     this.age = age;
     this.job = job;
     this.friends = ["Shelby", "Court"];
} 

Person.prototype = {
     constructor: Person,
     sayName: function () {
          alert(this.name);
     }
};

var person = new Person('Greg', 27, 'Doctor');

Knowing how to create objects, how do we inherit objects in Javascript?

Three, inheritance
1. Prototype Chain
The relationship between constructor, prototype and instance: Each constructor also has a prototype object. The prototype object contains a pointer to the constructor, and the instance contains an internal pointer to the prototype object. So what happens if we make the prototype object equal to another type of instance? Obviously, the prototype object at this time will contain a pointer to another prototype, and correspondingly, another prototype also contains a pointer to a constructor. If another prototype is another instance of another type, then the appeal relationship will still hold. Let's take a look at the following example.

function SuperType() {
    this.property = true;
}

SuerType.prototype.getSuperValue = function () {
     return this.property;
};

function SubType() {
    This.subProperty = false;
}

// Inherited SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function () {
    return this.subProperty();
};

var instance = new SubType();
alert(instance.getSuperValue); // true
In this example, SubType inherits SuperType, while SuperType inherits Object. If you want to determine the relationship between the prototype and the instance, you can use the instanceOf operator and the isPropertyOF() method.
For example instance instance of object; // true
Object.prototype isPropertype(instance); // true

However, when implementing inheritance through prototype chains, we can not use object literals to create prototype methods.

function SuperType() {
    this.property = true;
}

SuerType.prototype.getSuperValue = function () {
     return this.property;
};

function SubType() {
    This.subProperty = false;
}

// Inherited SuperType
SubType.prototype = new SuperType();

// Using literal quantities to add new methods can invalidate the previous line of code
SubType.prototype = {
      getSubValue: function () {
            return this.subproperty;
       },
      someOtherMethod: function () {
            return false;
      }
};

var instance = new SubType();
alert(instance.getSuperValue); // error

Next, let's talk about prototype chain inheritance.
First, the main problem comes from prototypes that contain reference type values. Prototype attributes containing reference type values are shared by all instances, which is why attributes are defined in constructors rather than in prototype objects.
Second, when creating an instance of a subtype, you cannot pass parameters to a supertype constructor.

2. Borrowing constructors

function SuperType(name) {
       this.name = name;
}

function SubType() {
    // It inherits SuperType and passes parameters.
    SuperType.call(this, "Nicholas");
    this.age = 29;
}

var instance = new SubType();
alert(instance.name);  // "Nicholas" 

The problem of borrowing constructors, methods are defined in constructors, so function reuse is impossible to talk about. Moreover, the method defined in the supertype prototype is not visible to the subtypes, and as a result, all types can only use the constructor pattern. Considering these problems, the technique of borrowing constructors is seldom used alone.

3. Combinatorial Inheritance
Combinatorial inheritance refers to an inheritance mode that combines prototype chain and borrowed constructor technology together to make full use of their advantages. The idea behind it is to use prototype chains to achieve performance of prototype attributes and enlargement, and to inherit instance attributes by borrowing constructors.

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"]
} 

SuperType.prototype.sayName = function () {
     alert[this.name];
};

function SubType(name, age) {
     // Inheritance attribute
     SuperType.call(this, name);
     this.age = age;
} 

// Inheritance method
SubType.prototype = new SuperType();

SubType.prototype.sayAge = function () {
    alert(this.age);
};


var instance = new SubType("Nicholas", 29);
instance.colors.push("black");
alert(instance.colors); // red, blue, green, black

Combinatorial inheritance avoids the drawbacks of prototype chains and borrowed constructors, combines their advantages and becomes the most common inheritance mode in Javascript.

4. Prototype Inheritance

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
]

var person = {
    Name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";

ECMAScipt5 standardizes prototype inheritance by adding Object.create().
Next to the above example:

 var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";

5. Parasitic Inheritance
Parasitic inheritance is an idea closely related to prototype inheritance. It is similar to factory mode, that is, to create a function only for encapsulation process, which enhances the object internally in some way, and finally returns the object as it really does all the work.
For example:

function createAnother(original) {
     var clone = object(original);
     clone.sayHi = function () {
           alert('hi');
     };
     return clone; 
}

var person = {
    name: 'Nochplas',
    friends: ['Shelby', 'Court', 'Van']
};

var anotherPerson = createAnother(person);

6,Combinatorial parasitic inheritance

Composite inheritance is the most common inheritance pattern in Javascript, but the biggest problem with composite inheritance is that in any case, two supertype constructors are called: one is to create a prototype of a subtype, the other is to die inside the subtype constructor.

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"]
} 

SuperType.prototype.sayName = function () {
     alert[this.name];
};

function SubType(name, age) {. 
     // Inheritance attribute
     SuperType.call(this, name);   // The second call to SuperType()
     this.age = age;
} 

// Inheritance method
SubType.prototype = new SuperType(); // The first call to SuperType()

SubType.prototype.sayAge = function () {
    alert(this.age);
};

The so-called parasitic combinatorial inheritance is to inherit attributes by borrowing constructors and inherit methods by mixing prototype chains. There is no need to call a supertype constructor to make a prototype of a subtype. All we need is a copy of the supertype prototype. Essentially, parasitic inheritance is used to inherit super-typed prototypes, and then the results are formulated into sub-typed prototypes. As follows:

function inheritPrototype (subType, superType) {
   var prototype = object (superType.prototype); // The preceding prototype inheritance object method
   prototype.constructor = subtype;
   subtype.prototype = prototype;
}


function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"]
} 

SuperType.prototype.sayName = function () {
     alert[this.name];
};

function SubType(name, age) {. 
     // Inheritance attribute
     SuperType.call(this, name);  
     this.age = age;
} 

inheritPrototype(Subtype, SuperType);

SubType.prototype.sayAge = function () {
    alert(this.age);
};


Posted by cyclops on Tue, 14 May 2019 23:31:21 -0700