Personal notes

Understanding closures (previous article)

There are situations like this: Function in the current context of EC (fn) set up a “heap memory” (a function or object), was beyond the current context variable (or other things) occupied (reference), the current context is not to be released in the stack (no matter what is the function of external references in the heap memory or object have used in the current context private variables).

The private variables inside are protected from outside interference by the private context.

It is possible to form an unreleased context in which private variables and values are stored for “lower” contexts to read

We call this saving/protecting mechanism of functions closures

High-level singleton design patterns (early modular ideas)

Another role for objects (singleton design pattern)

Another function of the object is to summarize the attributes and methods describing the same thing into the same space, which also plays a role in preventing global pollution. This is the singleton design pattern.

The singleton design pattern is an idea. The singleton design pattern in JS is an object with a single instance to manage the storage of variables

  • Each Object is an instance of the Object class (singleton)
  • The followingperson1Instead of calling it the object name, I’m calling itThe namespace(A space with a name)
var name = "Jade gentle";// Contaminate global variables
var age = 18;

var name = "Britain-based authority.";
var age = 81; 
//-------------
var person1 = {// Use namespaces to avoid polluting global variables
    name: 'jade gentle'.age: 18
};

var person2 = {
    name: 'britain-based authority'.age: 81
};
Copy the code

Closure + simple design pattern = advanced singleton design pattern

Application of simple profit design pattern:

let searchModule = (function () {
    let wd = "";

    function query() {
        // ...
    }

    function submit() {
        // ...
    }

    return {
        // submit:submitsubmit, query }; }) ();let weatherModule = (function () {
    let city = "";

    function submit() {
        // ...
    }

    return{ submit }; }) ();let skinModule = (function () {
    let wd = "";

    function search() {
        // ...
    }

    searchModule.submit();

    return{}; }) ();Copy the code

This was the early modular approach

Features are:

  1. Avoid global variable contamination based on closures
  2. To realize the mutual invocation of methods between different sections: return objects, expose methods that need to be called by others to the global,window.xxx=xxx(In the case of high exposure, there is still global contamination)

The idea of closure + singleton design is the high-level singleton design pattern (the early idea of modularity)

The inertia function

Another application of JS functional programming is lazy functions, which indicate that the branch of a function’s execution is executed only when the function is first called

Example 1: Get the element style

Get element style

  • Elements. Style. XXXGets the inline style
  • Box model attribute “plus:getBoundingClientRectGet the cross bit information between the current element and the current visible window.
  • Get all browser-computed styles (all rendered styles)
    • Standard:getComputedStyle
    • 6 ~ 8:currentStyle
let getCss = function (ele, attr) {
    if (typeofgetComputedStyle ! = ="undefined") {
       return window.getComputedStyle(ele)[attr];
    } 
    return ele.currentStyle[attr];
};
Copy the code

Each time a method is called, a browser-compatible judgment is performed, not the second time

let isCompatible = typeofgetComputedStyle ! = ="undefined"
let getCss = function (ele, attr) {
    if (isCompatible) {
       return window.getComputedStyle(ele)[attr];
    } 
    return ele.currentStyle[attr];
};
Copy the code

Extract the judgment logic, but you still need a logical judgment each time

// Core: function refactoring "closure"
let getCss = function (ele, attr) {
    if (typeofgetComputedStyle ! = ="undefined") {
        getCss = function (ele, attr) {
            return window.getComputedStyle(ele)[attr];
        };
    } else {
        getCss = function (ele, attr) {
            return ele.currentStyle[attr];
        };
    }
    // Make sure you get the value the first time
    return getCss(ele, attr);
};
Copy the code

The current function execution (outermost getCss) forms a private context. The declared small function in the private context is occupied by variables in the global context (outermost getCss), and the outermost getCss execution context is not destroyed, so the closure is formed.

Criteria for the emergence of lazy functions: Function refactoring occurs and “closures” are formed to improve performance

Example 2 Bind events

The js lazy function idea introduces the binding event:

function emit(element, type, func) {
    if (element.addEventListener) {
        element.addEventListener(type, func, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + type, func);
    } else { // If DOM2 events are not supported
        element['on'+ type] = func; }}Copy the code
function emit(element, type, func) {
    if (element.addEventListener) {
        emit = function (element, type, func) {
            element.addEventListener(type, func, false);
        };
    } else if (element.attachEvent) {
        emit = function (element, type, func) {
            element.attachEvent('on' + type, func);
        };
    } else {
        emit = function (element, type, func) {
            element['on' + type] = func;
        };
    }
    emit(element, type, func);
}
Copy the code

Function chemistry

“Take advantage of the preservation function of closures: anything that forms a closure to store some information for use by its subordinate context is an kerochemical idea.”

Conceptual understanding and application of the Idea of Corrification refer here

Call a function by passing it only a few arguments and have it return a function to process the rest.

The Currization example

Example 1: Implement FN

let total = fn(1.2) (3);
console.log(total); / / = > 6
Copy the code

The answer:

 const fn = (. params) = > {
    / / params - > [1, 2]
    return (. args) = > {
        // args->[3]
        return params.concat(args).reduce((total, item) = > {
            return total + item;
        });
    };
};
Copy the code

Resolution:

Converting an object to a raw value, or a string, calls the following methods in turn:

Symbol.toPrimitive

Object.prototype.valueOf()

Object.prototype.toString()

function fn() {}
alert(fn); // fn will be changed toString and output (fn.tostring () will be called)
// console.log(fn); // Console. log will also convert fn to String, but the console output and alert output will look different
Copy the code

The following refactoring toString method prints OK

function fn() {}
// Refactoring the toString method prints OK
fn.toString = function () {
    return 'ok';
}
alert(fn);
Copy the code

The following output is 11

function fn() {}
// toString/valueOf
fn[Symbol.toPrimitive] = function () {
    return 10;
}; 
console.log(fn+1)
Copy the code
fn.toString = function () {
    return 'ok';
}
Copy the code

Example 2: Implement an Add method so that the results meet the following expectations:


add(1) (2) (3) = 6;
add(1.2.3) (4) = 10;
add(1) (2) (3) (4) (5) = 15;
Copy the code

let add = curring();
let res = add(1) (2) (3);
console.log(res); / / - > 6

add = curring();
res = add(1.2.3) (4);
console.log(res); / / - > 10

add = curring();
res = add(1) (2) (3) (4) (5);
console.log(res); / / - > 15
Copy the code

Example 1 is definitely executed twice. The following example can be executed any time

Answer 1:

const curring = () = > {
    let arr = [];// Save params passed in each time the add is executed
    const add = (. params) = > {
        arr = arr.concat(params);
        return add;// Return add each time
    };
    add.toString = () = > {// The toString method of add is called
        return arr.reduce((total, item) = > {
            return total + item;
        });
    };
    return add;
};
Copy the code
  1. Declare aarrArray, put each timeaddIt was passed in during executionparamsPut it in here for now
  2. addAfter each execution, it returns each timeaddItself, so that he can call again
  3. At the end of the day, the output is stilladdAnd the outputaddIs equivalent to callingaddthetoStringMethod, so let me rewrite ittoStringMethod to sum the array

Example 3: Implement a curring method that satisfies the following expectations:

let add = curring(5);
res = add(1) (2) (3) (4) (5);
console.log(res); / / - > 15
Copy the code

Specify curring(n), where n is the number of times the returned function is executed. For example, if n=5, then add will be executed 5 times

If it reaches the specified number of times, it returns the result of the processing, otherwise it returns the function and continues to call. The above problem uses toString because it does not know how many times to execute the function, so when the function is not executed, it directly outputs the function and calls the toString method

const curring = n= > {
    let arr = [],
        index = 0;
    const add = (. params) = > {
        index++;
        arr = arr.concat(params);
        if (index >= n) {ToString = toString (); toString = toString (); toString = toString ()
            return arr.reduce((total, item) = > {
                return total + item;
            });
        }
        return add;
    };
    return add;
};
Copy the code

Combination function

Composite functions can also be called flattening of inline calls to functions or pipelining of functions

One of the most important concepts in functional programming is function composition, which is essentially piping together functions that process data and then passing the data through the pipe to get the final result. Such as:

 const add1 = x= > x + 1;
 const mul3 = x= > x * 3;
 const div2 = x= > x / 2;
 div2(mul3(add1(add1(0)))); / / = > 3
Copy the code

For example, compose returns a function that takes any number of functions as arguments (each of which takes a single argument). For example, compose returns a single function:

    const operate = compose(div2, mul3, add1, add1)
    operate(0) Div2 (mul3(add1(add1(0))))
    operate(2) Div2 (mul3(add1(add1(2)))))
Copy the code

Compose (f, g, h)(x), for example, compose(f, g, h)(x), for example, compose(f, g, h)(x)

const add1 = x= > x + 1;
const mul3 = x= > x * 3;
const div2 = x= > x / 2;
function compose() {}let operate = compose(div2, mul3, add1, add1);
console.log(operate(0));
Copy the code

The answer to 1

const add1 = x= > x + 1;
const mul3 = x= > x * 3;
const div2 = x= > x / 2;

function compose(. funcs) {
    // funcs -> [div2, mul3, add1, add1]
    return function operate(x) {
        let len = funcs.length;
        if (len === 0) return x;
        if (len === 1) return funcs[0](x);
        return funcs.reduceRight((result, item) = > {
            return item(result);
        }, x);
    };
}
let operate = compose(div2, mul3, add1, add1);
console.log(operate(0));
Copy the code

For example, the compose execution will store the incoming function up front, and wait until the compose execution is complete and then execute the returned function

Take a look atreduxIn thecomposeThe source code

A change on

function compose(. funcs) {
    if (funcs.length === 0) {
        return x= > {
            return x;
        };
    }
    if (funcs.length === 1) {
        return funcs[0];
    }
    // funcs -> [div2, mul3, add1, add1]
    return funcs.reduce((a, b) = > {
        // For the first time, each iteration of the callback generates a closure that stores a/ B and returns the a/ B used by the smaller function
        // a -> div2
        // b -> mul3
        // return x=>a(b(x)) @1
        / / the second time
        // a -> @1
        // b -> add1
        // return x=>a(b(x)) @2
        / / the third time
        // a -> @2
        // b -> add1
        // return x=>a(b(x)) @3
        return x= > {
            return a(b(x));
        };
    }); //=>return @3; Assign to outer Operate
}
const operate = compose(div2, mul3, add1, add1);
console.log(operate(0)); 
Copy the code

This redux notation creates a large number of closures that are not released during reduce, and finally operate(0) is released layer by layer. Answer 1 simply passes the result of the last execution to the next one, and the memory is freed after the last execution. So from this point of view, answer number one is going to perform better

Redux performs better by placing the judgment that funcs’ lengths are equal to 0 and 1 outside the iteration and not participating in the loop

Redux’s compose function is composed by redux’s compose function.

const add1 = x= > x + 1;
const mul3 = x= > x * 3;
const div2 = x= > x / 2;

function compose(. funcs) {
    let len = funcs.length;
    if (len === 0) return x= > x;/ / will be
    if (len === 1) return funcs[0];
    return function operate(x) {
        return funcs.reduceRight((result, item) = > {
            return item(result);
        }, x);
    };
}
let operate = compose(div2, mul3, add1, add1);
console.log(operate(0));
Copy the code

Extension: How can OPERATE require multiple parameters to be passed?

const add1 = x= > x + 1;
const mul3 = x= > x * 3;
const div2 = x= > x / 2;

function compose(. funcs) {
    let len = funcs.length;
    if (len === 0) return x= > x;/ / will be
    if (len === 1) return funcs[0];
    return function operate(. args) {
        return funcs.reduceRight((result, item) = > {
            if (Array.isArray(result)) {// If it is an array, it is multiple parameters
                returnitem(... result); }return item(result);
        }, args);
    };
}
let operate = compose(div2, mul3, add1, add1);
console.log(operate(0));
Copy the code

Rewrite the reduce

Array.prototype.reduce = function reduce(callback, initial) {
    let self = this.// this -> arr
        i = 0,
        len = self.length,
        item,
        result;
    if (typeofcallback ! = ="function") throw new TypeError('callback must be an function! ');
    if (typeof initial === "undefined") {
        // The initial value is not set, so that the initial value is the first item of the array, and traversal from the second item of the array
        initial = self[0];
        i = 1;
    }
    result = initial;

    // Loop through each item in the array
    for (; i < len; i++) {
        item = self[i];
        result = callback(result, item, i);
    }
    return result;
};

let arr = [10.20.30.40];
console.log(arr.reduce((result, item, index) = > {
    return result + item;
}));
console.log(arr.reduce((result, item) = > {
    return result + item;
}, 0));
Copy the code