An in depth understanding of the JavaScript series (50) : the Function pattern (part 2)

  • 2020-05-10 17:44:25
  • OfStack

introduce

Some of the patterns we introduced in this article are called initialization patterns and performance patterns. They are mainly used for initialization and performance improvement. Some of the patterns have been mentioned before.

Functions that are executed immediately

We've already described similar functions in detail in the fourth article in this series, "function expressions that are called immediately," but we'll just give you two more simple examples to summarize.


// Once the function is declared, it is executed immediately
(function () {
    console.log('watch out!');
} ()); // Functions declared this way can also be executed immediately
!function () {
    console.log('watch out!');
} (); // The following ways can also be oh
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();

Object initialization for immediate execution

This pattern means that when an object (not a function) is declared, one of the methods in the object is executed immediately for initialization. This pattern can be used for one-time code execution.


({
    // Here you can define constants and set other values
    maxwidth: 600,
    maxheight: 400,     //  You can define it, of course utility methods
    gimmeMax: function () {
        return this.maxwidth + "x" + this.maxheight;
    },     // Initialize the
    init: function () {
        console.log(this.gimmeMax());
        // More code ...
    }
}).init();  // So that's the initialization

Branch initialization

Branch initialization refers to initializing different code according to different conditions (scenarios) at the time of initialization, which is called conditional statement assignment. Previously, when we were doing event handling, we usually used the following code:


var utils = {
    addListener: function (el, type, fn) {
        if (typeof window.addEventListener === 'function') {
            el.addEventListener(type, fn, false);
        } else if (typeof document.attachEvent !== 'undefined') {
            el.attachEvent('on' + type, fn);
        } else {
            el['on' + type] = fn;
        }
    },
    removeListener: function (el, type, fn) {
    }
};

First, we need to define two interfaces, one for add event handle and one for remove event handle. The code is as follows:


var utils = {
    addListener: null,
    removeListener: null
};

The implementation code is as follows:

if (typeof window.addEventListener === 'function') {
    utils.addListener = function (el, type, fn) {
        el.addEventListener(type, fn, false);
    };
} else if (typeof document.attachEvent !== 'undefined') { // IE
    utils.addListener = function (el, type, fn) {
        el.attachEvent('on' + type, fn);
    };
    utils.removeListener = function (el, type, fn) {
        el.detachEvent('on' + type, fn);
    };
} else { // Other old browsers
    utils.addListener = function (el, type, fn) {
        el['on' + type] = fn;
    };
    utils.removeListener = function (el, type, fn) {
        el['on' + type] = null;
    };
}

Is it convenient to use it? The code is much more elegant.

Self-declared function

1 usually overrides the code of the function with the same name inside the function, such as:


var scareMe = function () {
    alert("Boo!");
    scareMe = function () {
        alert("Double boo!");
    };
};

This kind of code is very confusing. Let's take a look at the results of the example first:

// 1. Add a new property
scareMe.property = "properly";
// 2. scareMe Fu and 1 A new value
var prank = scareMe;
// 3. As a 1 Method calls
var spooky = {
    boo: scareMe
};
// The call is made with the new variable name
prank(); // "Boo!"
prank(); // "Boo!"
console.log(prank.property); // "properly"
// Calls are made using methods
spooky.boo(); // "Boo!"
spooky.boo(); // "Boo!"
console.log(spooky.boo.property); // "properly"

As a result of the execution, you can see that when you assign a defined function to a new variable (or an internal method), the code does not execute the overloaded scareMe code, as opposed to the following example:

// Use self-declaring functions
scareMe(); // Double boo!
scareMe(); // Double boo!
console.log(scareMe.property); // undefined

You must be very careful when you use this mode, otherwise the actual result may not be the same as what you expect. Of course, you can also use this special method to do some special operations.

Memory optimization

This pattern takes advantage of the properties of functions to avoid a lot of double counting. The code usually looks like this:


var myFunc = function (param) {
    if (!myFunc.cache[param]) {
        var result = {};
        // ... A complex operation ...
        myFunc.cache[param] = result;
    }
    return myFunc.cache[param];
}; // cache storage
myFunc.cache = {};

However, there is a problem with the above code. If the parameter passed in is toString or some other common method like Object has, there will be a problem. In this case, the legendary hasOwnProperty method needs to be used.

var myFunc = function (param) {
    if (!myFunc.cache.hasOwnProperty(param)) {
        var result = {};
        // ... A complex operation ...
        myFunc.cache[param] = result;
    }
    return myFunc.cache[param];
}; // cache storage
myFunc.cache = {};

Or if you pass in more than one parameter, you can store these parameters through JSON's stringify method to produce one cachekey value. The code is as follows:

({
    // Here you can define constants and set other values
    maxwidth: 600,
    maxheight: 400,     //  You can define it, of course utility methods
    gimmeMax: function () {
        return this.maxwidth + "x" + this.maxheight;
    },     // Initialize the
    init: function () {
        console.log(this.gimmeMax());
        // More code ...
    }
}).init();  // So that's the initialization
0
Or if there are multiple parameters, arguments.callee features can also be used:

var myFunc = function (param) {
    var f = arguments.callee,
        result;
    if (!f.cache[param]) {
        result = {};
        // ... A complex operation ...
        f.cache[param] = result;
    }
    return f.cache[param];
}; // cache storage
myFunc.cache = {};

conclusion

You don't have to summarize it, you just have to look at the code


Related articles: