1. Determine the concrete type of any object
As we all know, there are six primitive data types (Boolean, Number, String, Null, Undefined, Symbol) and one object data type in JavaScript. But did you know that object data types can be subdivided into many subtypes? An object may be an array, function, map, etc. If we want to get the specific type of the object, what should we do?
code:
function toRawType (value) { let _toString = Object.prototype.toString; let str = _toString.call(value) return str.slice(8, -1)}
Explanation
ECMAScript has the following rules:
For different objects, calling Object.prototype.toString() will return different results.
Moreover, the return value of Object.prototype.toString() is always in the format of '[object'+'tag'+']'. If we only want the label in the middle, we can remove the characters on both sides by regular expression or String.prototype.slice().
example:
toRawType(null) // "Null"toRawType(/sdfsd/) //"RegExp"
2. Cache function calculation results
If there is a function like this:
function computed(str) { // Suppose the calculation in the funtion is very time consuming console.log('2000s have passed') return 'a result'}
We want to cache the result of the function operation. When calling later, if the parameters are the same, the function will not be executed, but the result in the cache will be returned directly. what can we do?
code:
function cached(fn){ // Create an object to store the results returned after each function execution. const cache = Object.create(null); // Returns the wrapped function return function cachedFn (str) { // If the cache is not hit, the function will be executed if ( !cache[str] ) { let result = fn(str); // Store the result of the function execution in the cache cache[str] = result; } return cache[str] }}
example:
3. Realize Array.prototype.map
This is a useful built-in method in JavaScript, and you should be able to implement this functionality yourself.
code:
const selfMap = function (fn, context) { let arr = Array.prototype.slice.call(this) let mappedArr = Array() for (let i = 0; i < arr.length; i++) { if (!arr.hasOwnProperty(i)) continue; mappedArr[i] = fn.call(context, arr[i], i, this) } return mappedArr} Array.prototype.selfMap = selfMap;
example:
4. Implement Array.prototype.filter
This is a useful built-in method in JavaScript, and you should be able to implement this functionality yourself.
code:
const selfFilter = function (fn, context) { let arr = Array.prototype.slice.call(this) let filteredArr = [] for (let i = 0; i < arr.length; i++) { if(!arr.hasOwnProperty(i)) continue; fn.call(context, arr[i], i, this) && filteredArr.push(arr[i]) } return filteredArr} Array.prototype.selfFilter = selfFilter;
example:
5. Implement Array.prototype.some
This is a useful built-in method in JavaScript, and you should be able to implement this functionality yourself.
code:
const selfSome = function (fn, context) { let arr = Array.prototype.slice.call(this) if(!arr.length) return false for (let i = 0; i < arr.length; i++) { if(!arr.hasOwnProperty(i)) continue; let res = fn.call(context,arr[i],i,this) if(res)return true } return false} Array.prototype.selfSome = selfSome;
example:
6. Implement Array.prototype.reduce
This is a useful built-in method in JavaScript, and you should be able to implement this functionality yourself.
code:
const selfReduce = function (fn, initialValue) { let arr = Array.prototype.slice.call(this) let res let startIndex if (initialValue === undefined) { for (let i = 0; i < arr.length; i++) { if (!arr.hasOwnProperty(i)) continue startIndex = i res = arr[i] break } } else { res = initialValue } for (let i = ++startIndex || 0; i < arr.length; i++) { if (!arr.hasOwnProperty(i)) continue res = fn.call(null, res, arr[i], i, this) } return res} Array.prototype.selfReduce = selfReduce;
example:
7. Implement Array.prototype.flat
code:
const selfFlat = function (depth = 1) { let arr = Array.prototype.slice.call(this) if (depth === 0) return arr return arr.reduce((pre, cur) => { if (Array.isArray(cur)) { return [...pre, ...selfFlat.call(cur, depth - 1)] } else { return [...pre, cur] } }, [])} Array.prototype.selfFlat = selfFlat;
example:
8. Currying
Currying is a technique for evaluating a function with multiple arguments as a sequence of functions with a single argument.
In other words, when instead of taking all arguments at once, a function takes the first argument and returns a new function that takes the second argument and returns a new function that takes the third argument, and so on And so on until all parameters have been fulfilled.
That is we transform the function call add(1,2,3) into add(1)(2)(3) . By using this technique, small blocks can be easily configured and reused.
Why is it useful?
-
Currying helps you avoid passing the same variable over and over again.
-
It helps to create higher order functions and it is very helpful for event handling.
-
Widgets can be easily configured and reused.
Let's look at a simple add function. It takes three operands as arguments and returns the sum of all three operands as a result.
function add(a,b,c){ return a + b + c;}
You can call it with too few (weird results) or too many (excess arguments are ignored).
add(1,2,3) --> 6 add(1,2) --> NaNadd(1,2,3,4) --> 6 //Extra parameters will be ignored.
How do I convert an existing function to a curried version?
code:
function curry(fn) { if (fn.length <= 1) return fn; const generator = (...args) => { if (fn.length === args.length) { return fn(...args) } else { return (...args2) => { return generator(...args, ...args2) } } } return generator}
example:
9. Debounce
Debounce is nothing more than reducing unnecessary time-consuming calculations to improve browser performance. In some cases, certain functions require more time to perform an operation. For example, take the example of a search bar on an e-commerce site.
Let's say the user wants to get the "Tutorix Learning Kit". He types every character of the product into the search bar. After each character is entered, an Api call is made from the browser to the server to fetch the desired product. Since he wants "Tutorix Learning Kit", user has to make 17 Api calls from browser to server.
Imagine a scenario when millions of people do the same search which calls billions of APIs. So calling billions of APIs at once will definitely slow down the browser performance. To reduce this shortcoming, debounce came into being.
In this case, debounce will set a time interval between keystrokes, let's say 2 seconds. Api calls will only be made if more than 2 seconds elapse between keystrokes. During these 2 seconds, user can input at least some characters, reducing these characters for Api calls. Browser performance will improve due to fewer Api calls. It is important to note that the Debouncing function is updated with every keystroke.
code:
const debounce = (func, time = 17, options = { leading: true, context: null}) => { let timer; const _debounce = function (...args) { if (timer) { clearTimeout(timer) } if (options.leading && !timer) { timer = setTimeout(null, time) func.apply(options.context, args) }else{ timer = setTimeout(() => { func.apply(options.context, args) timer = null }, time) } }; _debounce.cancel = function () { clearTimeout(timer) timer = null }; return _debounce};
10. Throttling
Throttling will alter the function in such a way that it can fire at most once in an interval. For example, no matter how many times the user clicks the button, the limit will only execute the function once in 1000 milliseconds.
code:
const throttle = (func, time = 17, options = { leading: true, trailing: false, context: null}) => { let previous = new Date(0).getTime() let timer; const _throttle = function (...args) { let now = new Date().getTime(); if (!options.leading) { if (timer) return timer = setTimeout(() => { timer = null func.apply(options.context, args) }, time) } else if (now - previous > time) { func.apply(options.context, args) previous = now } else if (options.trailing) { clearTimeout(timer) timer = setTimeout(() => { func.apply(options.context, args) }, time) } }; _throttle.cancel = () => { previous = 0; clearTimeout(timer); timer = null }; return _throttle};
11. Lazy loading images
Lazy loading images means that images are loaded asynchronously on the site - that is, after the above-the-fold content has fully loaded, or even conditionally, only when they appear in the browser's viewport. the
This means that images placed at the bottom of the page won't even be loaded if the user doesn't scroll all the way down.
code:
// getBoundingClientRectlet imgList1 = [...document.querySelectorAll(".get_bounding_rect")]let num = imgList1.length let lazyLoad1 = (function () { let count = 0 return function () { let deleteIndexList = [] imgList1.forEach((img,index) => { let rect = img.getBoundingClientRect() if (rect.top < window.innerHeight) { img.src = img.dataset.src // Add picture to delete list after loading successfully deleteIndexList.push(index) count++ if (count === num) { //When all pictures are loaded, unbind scroll event document.removeEventListener('scroll',lazyLoad1) } } }) // Delete loaded pictures imgList1 = imgList1.filter((_,index)=>!deleteIndexList.includes(index)) }})()
12. The array is random and unordered
We often need to shuffle an array.
code:
// Randomly select one of all elements after the current element to exchange with the current elementfunction shuffle(arr) { for (let i = 0; i < arr.length; i++) { let randomIndex = i + Math.floor(Math.random() * (arr.length - i)); [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]] } return arr} // Generate a new array, randomly take an element from the original array and put it into the new arrayfunction shuffle2(arr) { let _arr = [] while (arr.length) { let randomIndex = Math.floor(Math.random() * (arr.length)) _arr.push(arr.splice(randomIndex, 1)[0]) } return _arr}
example:
13. Singleton mode
The singleton pattern limits the number of instances of a particular object to one, and this single instance is called the singleton pattern.
Singletons are useful in situations where system-wide operations need to be coordinated from a single central location. An example is database connection pooling. The pool manages the creation, destruction and lifecycle of all database connections throughout the application, ensuring that no connections are "lost".
Singletons reduce the need for global variables, which is especially important in JavaScript because it limits namespace pollution and the associated risk of name collisions.
code:
function proxy(func) { let instance; let handler = { construct(target, args) { if (!instance) { // Create an instance if there is not exist instance = Reflect.construct(func,args) } return instance } } return new Proxy(func, handler)} // example function Person(name, age) { this.name = name this.age = age} const SingletonPerson = proxy(Person) let person1 = new SingletonPerson('zhl', 22) let person2 = new SingletonPerson('cyw', 22) console.log(person1 === person2) // true
example:
14. Implement JSON.stringify
This is a useful built-in method in JavaScript, and you should be able to implement this functionality yourself.
code:
const isString = value => typeof value === 'string';const isSymbol = value => typeof value === 'symbol'const isUndefined = value => typeof value === 'undefined'const isDate = obj => Object.prototype.toString.call(obj) === '[object Date]'const isFunction = obj => Object.prototype.toString.call(obj) === '[object Function]';const isComplexDataType = value => (typeof value === 'object' || typeof value === 'function') && value !== null;const isValidBasicDataType = value => value !== undefined && !isSymbol(value); const isValidObj = obj => Array.isArray(obj) || Object.prototype.toString.call(obj) === '[object Object]';const isInfinity = value => value === Infinity || value === -Infinity // Symbol,undefined,function in array will become null// Infinity,NaN will also become nullconst processSpecialValueInArray = value => isSymbol(value) || isFunction(value) || isUndefined(value) || isInfinity(value) || isNaN(value) ? null : value; // Handling property values according to JSON specificationconst processValue = value => { if (isInfinity(value) || isNaN(value)) { return null } if (isString(value)) { return `"${value}"` } return value}; // obj.loop = obj const jsonStringify = (function () { // Closure + WeakMap prevent circular references let wp = new WeakMap(); //It is the function in the closure that recursively calls jsonstrify, not the jsonstrify function declared by const return function jsonStringify(obj) { if (wp.get(obj)) throw new TypeError('Converting circular structure to JSON'); let res = ""; if (isComplexDataType(obj)) { if (obj.toJSON) return obj.toJSON; if (!isValidObj(obj)) { return } wp.set(obj, obj); if (Array.isArray(obj)) { res += "["; let temp = []; obj.forEach((value) => { temp.push( isComplexDataType(value) && !isFunction(value) ? jsonStringify(value) : `${processSpecialValueInArray(value, true)}` ) }); res += `${temp.join(',')}]` } else { res += "{"; let temp = []; Object.keys(obj).forEach((key) => { if (isComplexDataType(obj[key])) { if (isValidObj(obj[key])) { temp.push(`"${key}":${jsonStringify(obj[key])}`) } else if (isDate(obj[key])) { temp.push(`"${key}":"${obj[key].toISOString()}"`) } else if (!isFunction(obj[key])) { temp.push(`"${key}":{}`) } } else if (isValidBasicDataType(obj[key])) { temp.push(`"${key}":${processValue(obj[key])}`) } }); res += `${temp.join(',')}}` } } else if (isSymbol(obj)) { return } else { return obj } return res }})(); // example let s = Symbol('s')let obj = { str: "123", arr: [1, {e: 1}, s, () => { }, undefined,Infinity,NaN], obj: {a: 1}, Infinity: -Infinity, nan: NaN, undef: undefined, symbol: s, date: new Date(), reg: /123/g, func: () => { }, dom: document.querySelector('body'),}; console.log(jsonStringify(obj));console.log(JSON.stringify(obj));
example:
Summarize
The above are the 14 JavaScript functions I shared with you. These functions are also what we must know as a web front-end developer. I hope they are useful to you. If you think they are helpful to you, please remember to like me and follow me , and share it with your friends who are doing development, maybe it can help him.