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 following
person1
Instead 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:
- Avoid global variable contamination based on closures
- 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. XXX
Gets the inline style- Box model attribute “plus:
getBoundingClientRect
Get 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
- Standard:
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
- Declare a
arr
Array, put each timeadd
It was passed in during executionparams
Put it in here for now add
After each execution, it returns each timeadd
Itself, so that he can call again- At the end of the day, the output is still
add
And the outputadd
Is equivalent to callingadd
thetoString
Method, so let me rewrite ittoString
Method 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 atredux
In thecompose
The 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