This is the 19th day of my participation in the Genwen Challenge

1. Constructor pattern

(1) THREE common methods of creating new objects in JS

var newObject = {};
 
var newObject = Object.create( Object.prototype );
 
var newObject = new Object(a);Object.create(null) is a simple Object with no other attributes.
Copy the code

(2) Four ways to create new attributes

newObject.someKey = "Hello World";
 
newObject["someKey"] = "Hello World";
 
Object.defineProperty( newObject, "someKey", {
    value: "for more control of the property's behavior".writable: true.enumerable: true.configurable: true
});
 
Object.defineProperties( newObject, {
  "someKey": {
    value: "Hello World".writable: true
  },
 
  "anotherKey": {
    value: "Foo bar".writable: false}});Copy the code



(3) usage

Common way (most practical)

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;
    this.output= function () {
        return this.model + "走了" + this.miles + "公里";
    };
}
 
var car_benchi = new Car('benchi'.'2017'.'300');
var car_aodi = new Car('aodi'.'2016'.'600');
 
console.log(car_benchi.output === car_benchi.output); //false
Copy the code

Why do we finally judge whether the function is the same object? Because I have read an article on the Internet before that this method will create multiple anonymous functions when creating a new object, causing memory problems. Note that this is the anonymous function, not the output variable.

Now we will implement the above new method as a normal function, because this is needed to compare to the Prototype & Factory mode method

function Car(model, year, miles) {
    var object = Object.create(null);
 
    /*prototype*/
    Object.defineProperty( object, "__proto__", {
        value: Car.prototype,
        writable: true.enumerable: false.configurable: false
    })
 
    object.model = model;
    object.year = year;
    object.miles = miles;
 
    object.output = function () {
        return function () {
            return this.model + "走了" + this.miles + "公里";
        }.call(this);
    }
 
    return object;
}
 
var car_benchi =  Car('benchi'.'2017'.'300');
var car_aodi =  Car('aodi'.'2016'.'600');
 
console.log(car_benchi.output === car_benchi.output); //false
Copy the code

Why is object.create (null) used for the created Object and object.defineProperty used for the __proto__ attribute?

Since objects created using new have prototype chains, I need to use __proto__, and the property is writable, non-enumerable, and non-configurable. The reason why it is not configurable is because when I use var object = {} / new object (), I use defineProperty and it doesn’t work. Therefore, object.create (null) is used. This is also noticed in my test today.

Note: __proto__ is generally not allowed during development, although most browsers now support it.



prototype

It is possible to solve the above problem by putting the output function in the outermost layer, but this is not maintainable and taints the global variable.

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;
}
 
Car.prototype.output= function () {
    return this.model + "走了" + this.miles + "公里";
};
 
var car_benchi = new Car('benchi'.'2017'.'300');
var car_aodi = new Car('aodi'.'2016'.'600');
 
console.log(car_aodi.output === car_benchi.output); //true
Copy the code

You can see that a new method has been added to Car. Prototype. Because the only common point for sharing multiple objects is to find the Car. Prototype on the prototype chain. Remember not to reassign Car. Prototype, because if you do, the prototype chain will be broken.

(4)

I’m using Prototype mostly for extensions to built-in JS objects, such as formatting and array variations, because it saves me time making changes for each object. The essence of a constructor, I think, is to find common ground in its prototype chain, because the prototype chain is common to all and is a JS native object. We don’t have to do it ourselves.

2. Factory mode

The factory model is analogous to the factory in real life, which can produce a large number of similar goods to do the same thing and achieve the same effect. But doesn’t it feel like I could have done it with the constructor pattern, and I could have the prototype chain.

Now suppose there is a factory that can only produce Mercedes Benz. Is its production process a factory mode or a constructor mode? It’s hard to say. Why? Because if it only produces one model at a time, then can we think of it as a constructor pattern, because now we’re doing the same thing, the same inputs and outputs. Can we directly regard this factory as a new one and produce a new Mercedes Benz every time? In this case, take a look at the new method above, which is implemented using ordinary functions. Our current factory is doing just that (if new is not applicable). In this case, I think the two are equal.

Now, when our factory is producing new products, it finds that the previous car has a good market, so when developing new car models, we have to produce the previous car, but now we can’t use the previous method. Because I need to have different inputs and outputs. So let’s rewrite this way

function Car(model, year, miles, version) {
    function carBasicInformation() {
        var object = Object.create(null);
 
        /*prototype*/
        Object.defineProperty( object, "__proto__", {
            value: Car.prototype,
            writable: true.enumerable: false.configurable: false
        })
 
        object.model = model;
        object.year = year;
        object.miles = miles;
 
        object.output = function () {
            return function () {
                return this.model + "走了" + this.miles + "公里";
            }.call(this);
        }
 
        return object;
    }
 
    // Model 0001 has a navigation system
    function Car_0001 () {
        var car_0001 = carBasicInformation(model, year, miles);
        car_0001.omnirange = 'omnirange_01'; 
 
        return car_0001;
    }
 
    // Model 0002 has no navigation system
    function Car_0002 () {
        var car_0002 = carBasicInformation(model, year, miles);
 
        return car_0002;
    }
 
    switch (version) {
        case '0001':
            return Car_0001();
        case '0002':
            return Car_0002();
        default:
            break; // Of course the factory will not do nothing.}}var aodi_0001 =  Car('benchi'.'2017'.'300'.'0001');
var aodi_0002 =  Car('aodi'.'2016'.'600'.'0002');
 
console.log(aodi_0001.omnirange); //omnirange_01
console.log(aodi_0002.omnirange); //undeinfed
Copy the code

I think that’s what the factory model is all about now, different inputs and outputs. One might ask, I could have just written this logic inside the previous new car(), so I’m still in constructor mode.

That’s true. So let’s analyze what the logic of this judgment is. This is a business logic, we will correspond the above code to the role of the factory, Car is the factory, Car_0001 and Car_0002 are two different groups. Judgment logic is the marketing staff. Car_0001 and Car_0002 are constructors, because I make the same thing, the judgment logic must be obtained by the marketing people analyzing the market demand. Car_0001 and Car_0002 should only do their own things according to object-oriented or modular development. After all, business logic determines the code structure of each car. If we put all the contents together, will the contents of CAR_0001 changed affect the contents of CAR_0002, which also violates the open-closed principle? Besides, when new models are produced in the future, do we need to change the codes of the two models that have been stabilized, which is very responsible to maintain? Referring to the open-closed principle: software entities should be extensible, not modifiable. That is, open for extension and closed for modification. So if there is a new model, we still need to modify the above code, because what, because the business logic determines that is so.

So the code above is not perfect and needs to be changed. I’m sure you remember the above information prototype. We can add new things to it without affecting other functions.

3. Abstract factory pattern

A further step to the factory pattern is the abstract factory. Now let’s think about the requirements above. You need to add a new version, but you don’t want to change the code that is already stable, but still have something in common.

/ / seeking common
function basicCarInformation(model, year, miles) { // This can be done without such complexity, but I am too lazy to copy the above code directly.
    var object = Object.create(null);
 
        /*prototype*/
        Object.defineProperty( object, "__proto__", {
            value: basicCarInformation.prototype,
            writable: true.enumerable: false.configurable: false
        })
 
        object.model = model;
        object.year = year;
        object.miles = miles;
 
        object.output = function () {
            return function () {
                return this.model + "走了" + this.miles + "公里";
            }.call(this);
        }
 
        return object;
}
 
inheritAbstactFactory.prototype.basicCarInformation = basicCarInformation;
 
/ / ground
function inheritAbstactFactory(option, version) {
    return function(model, year, miles) {
        var basicInformation = basicCarInformation(model, year, miles);
        basicInformation.option = option;
        basicInformation.version = version;
 
        returnbasicInformation; }}var CarFactory0001 = inheritAbstactFactory({omnirange : 'omnirange_01'}, 'aodi_0001');
 
var aodi_0001 =  CarFactory0001('benchi'.'2017'.'300');
aodi_0001.output();
Copy the code

Var CarFactory0001 = inheritAbstactFactory({omnirange: ‘omnirange_01’}, ‘aoDI_0001 ‘); Add a new version and you’re done.

Var basicInformation = basicCarInformation(model, year, miles); This line can be implemented as an inner function or by using a parasitic constructor. Here we use Prototype to compare object-oriented languages. Now basicCarInformation is the Base class, inheritAbstactFactory abstract class, and CarFactory0001 inherits the subclass of the abstract class. Of course you can use the other two approaches, because that’s not the focus of the factory pattern.

The above similarity is a constructor pattern, and the difference is the factory pattern (similarity is the foundation). They have different concerns.

4. Application of abstract factory

Factory patterns can be particularly useful when applied to:

  • When our object or component setup involves a high degree of complexity
  • When we need to easily generate different object instances depending on our environment
  • When we are dealing with many small objects or components that share the same properties
  • When composing objects using instances of other objects, only API contracts (aka duck typing) need to be fulfilled. This is useful for decoupling.

Analyze the above sentences:

  • The high degree of negative complexity refers to the above logical judgment
  • Change in demand
  • Agree to disagree, many shares are not all shares, because all shares directly construct the schema
  • The meaning here is that care is only the input and output, and does not care whether the object is related to the prototype chain, just like call and apply are used. It’s duck typing.