In depth understanding of the JavaScript series (45) : code reuse patterns (avoid section)

  • 2020-05-10 17:45:40
  • OfStack

introduce

Any programming code reuse is put forward, otherwise the words every time to develop a new program or writing a new function to write the new words, that is indeed, but the code reuse is also good to bad, we will focus on the next two article to discuss the code reuse, 1 article avoid article, refers to try to avoid using these patterns, because of some problems more or less bring 1; The second row is the recommended section, which refers to the mode recommended for everyone to use. Generally speaking, there will be no problem with 1.

Mode 1: default mode

It is often problematic for code to reuse the common default schema, which USES the constructor of Parent() to create an object and assign that object to the prototype of Child(). Let's look at the code 1:


function inherit(C, P) {
    C.prototype = new P();
} // Parent constructor
function Parent(name) {
    this.name = name || 'Adam';
}
// Add to the prototype say function
Parent.prototype.say = function () {
    return this.name;
};
// Child The constructor is empty
function Child(name) {
} // Perform inheritance
inherit(Child, Parent); var kid = new Child();
console.log(kid.say()); // "Adam" var kiddo = new Child();
kiddo.name = "Patrick";
console.log(kiddo.say()); // "Patrick" // disadvantages : Cannot allow parameters to be passed in Child The constructor
var s = new Child('Seth');
console.log(s.say()); // "Adam"

The disadvantage of this mode is that Child does not pass in the parameters and is essentially useless.

Pattern 2: borrow the constructor

In this mode, Child borrows Parent's constructor to apply, and then passes child's this and its parameters to apply's method:


// Parent constructor
function Parent(name) {
    this.name = name || 'Adam';
} // Add to the prototype say function
Parent.prototype.say = function () {
    return this.name;
}; // Child The constructor
function Child(name) {
    Parent.apply(this, arguments);
} var kid = new Child("Patrick");
console.log(kid.name); // "Patrick" // Disadvantage: no inheritance from the constructor say methods
console.log(typeof kid.say); // "undefined"

The disadvantage is also obvious: the say method is not available because it has not been inherited.

Pattern 3: borrow the constructor and set the stereotype

Both of the above two models have their own disadvantages, so how to remove the disadvantages of both? Let's try 1:


// Parent constructor
function Parent(name) {
    this.name = name || 'Adam';
} // Add to the prototype say function
Parent.prototype.say = function () {
    return this.name;
}; // Child The constructor
function Child(name) {
    Parent.apply(this, arguments);
} Child.prototype = new Parent(); var kid = new Child("Patrick");
console.log(kid.name); // "Patrick"
console.log(typeof kid.say); // function
console.log(kid.say()); // Patrick
console.dir(kid);
delete kid.name;
console.log(kid.say()); // "Adam"

When running, the 1 cut is normal, but did you notice that the Parent constructor was executed twice, so although the program is available, it is inefficient.

Pattern 4: Shared archetypes

Shared prototype means that Child and Parent use the same prototype. The code is as follows:


function inherit(C, P) {
    C.prototype = P.prototype;
} // Parent constructor
function Parent(name) {
    this.name = name || 'Adam';
} // Add to the prototype say function
Parent.prototype.say = function () {
    return this.name;
}; // Child The constructor
function Child(name) {
} inherit(Child, Parent); var kid = new Child('Patrick');
console.log(kid.name); // undefined
console.log(typeof kid.say); // function
kid.name = 'Patrick';
console.log(kid.say()); // Patrick
console.dir(kid);

Confirm that it is still the same, the parameter of Child was not received correctly.

Pattern 5: temporary constructor

First borrow the constructor, then set the Child stereotype as an instance of the borrowed constructor, and finally restore the constructor of the Child stereotype. The code is as follows:


/* closure */
var inherit = (function () {
    var F = function () {
    };
    return function (C, P) {
        F.prototype = P.prototype;
        C.prototype = new F();
        C.uber = P.prototype;
        C.prototype.constructor = C;
    }
} ()); function Parent(name) {
    this.name = name || 'Adam';
} // Add to the prototype say function
Parent.prototype.say = function () {
    return this.name;
}; // Child The constructor
function Child(name) {
} inherit(Child, Parent); var kid = new Child();
console.log(kid.name); // undefined
console.log(typeof kid.say); // function
kid.name = 'Patrick';
console.log(kid.say()); // Patrick
var kid2 = new Child("Tom");
console.log(kid.say());
console.log(kid.constructor.name); // Child
console.log(kid.constructor === Parent); // false

The problem is the same. Child cannot receive parameters normally.

Pattern 6: klass

For this mode, please enter the code first:


var klass = function (Parent, props) {     var Child, F, i;     // 1.
    // neoconstructor
    Child = function () {
        if (Child.uber && Child.uber.hasOwnProperty("__construct")) {
            Child.uber.__construct.apply(this, arguments);
        }
        if (Child.prototype.hasOwnProperty("__construct")) {
            Child.prototype.__construct.apply(this, arguments);
        }
    };     // 2.
    // inheritance
    Parent = Parent || Object;
    F = function () {
    };
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.uber = Parent.prototype;
    Child.prototype.constructor = Child;     // 3.
    // Add implementation method
    for (i in props) {
        if (props.hasOwnProperty(i)) {
            Child.prototype[i] = props[i];
        }
    }     // return the "class"
    return Child;
}; var Man = klass(null, {
    __construct: function (what) {
        console.log("Man's constructor");
        this.name = what;
    },
    getName: function () {
        return this.name;
    }
}); var first = new Man('Adam'); // logs "Man's constructor"
first.getName(); // "Adam" var SuperMan = klass(Man, {
    __construct: function (what) {
        console.log("SuperMan's constructor");
    },
    getName: function () {
        var name = SuperMan.uber.getName.call(this);
        return "I am " + name;
    }
}); var clark = new SuperMan('Clark Kent');
clark.getName(); // "I am Clark Kent" console.log(clark instanceof Man); // true
console.log(clark instanceof SuperMan); // true

How's that? Is it a little confusing to look at? To say the best, the grammar and specification of this mode is as different as that of other languages. Would you like to use it? Well...

conclusion

Although the above six modes realize some functions in some special cases, they all have their own disadvantages, so in general, we should avoid using them.


Related articles: