32 handwritten JS to consolidate your JS Foundation (high frequency interview)
As a front-end development, JS is the top priority. Recently, the peak of the interview has been ended. Basically, the offer has been decided. Wait for the lottery. Take this time to summarize 32 handwritten JS questions. These are high-frequency interview questions. I hope they can be helpful to you.
As for the source code, they all follow the specifications and can run through the MDN examples. Most of the rest will involve some application questions about JS and my interview process
01. Array flattening
Array flattening refers to changing a multi-dimensional array into a one-dimensional array
const arr = [1, [2, [3, [4, 5]]], 6]; // => [1, 2, 3, 4, 5, 6] Copy code
Method 1: use flat()
const res1 = arr.flat(Infinity); Copy code
Method 2: using regular
const res2 = JSON.stringify(arr).replace(/\[|\]/g, '').split(','); Copy code
But the data type will change to string
Method 3: regular improved version
const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']'); Copy code
Method 4: use reduce
const flatten = arr => { return arr.reduce((pre, cur) => { return pre.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []) } const res4 = flatten(arr); Copy code
Method 5: function recursion
const res5 = []; const fn = arr => { for (let i = 0; i < arr.length; i++) { if (Array.isArray(arr[i])) { fn(arr[i]); } else { res5.push(arr[i]); } } } fn(arr); Copy code
02. Array de duplication
const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}]; // => [1, '1', 17, true, false, 'true', 'a', {}, {}] Copy code
Method 1: use Set
const res1 = Array.from(new Set(arr)); Copy code
Method 2: two-layer for loop + splice
const unique1 = arr => { let len = arr.length; for (let i = 0; i < len; i++) { for (let j = i + 1; j < len; j++) { if (arr[i] === arr[j]) { arr.splice(j, 1); // Every time a tree is deleted, j -- ensure that the value of j remains unchanged after self addition. At the same time, len --, reduce the number of cycles and improve the performance len--; j--; } } } return arr; } Copy code
Method 3: using indexOf
const unique2 = arr => { const res = []; for (let i = 0; i < arr.length; i++) { if (res.indexOf(arr[i]) === -1) res.push(arr[i]); } return res; } Copy code
Of course, you can also use include and filter. The ideas are similar.
Method 4: use include
const unique3 = arr => { const res = []; for (let i = 0; i < arr.length; i++) { if (!res.includes(arr[i])) res.push(arr[i]); } return res; } Copy code
Method 5: using filter
const unique4 = arr => { return arr.filter((item, index) => { return arr.indexOf(item) === index; }); } Copy code
Method 6: using Map
const unique5 = arr => { const map = new Map(); const res = []; for (let i = 0; i < arr.length; i++) { if (!map.has(arr[i])) { map.set(arr[i], true) res.push(arr[i]); } } return res; } Copy code
03. Convert class array to array
Class array has the length attribute, but does not have methods on the array prototype. Common class arrays include arguments and the results returned by DOM operation methods.
Method 1: array from
Array.from(document.querySelectorAll('div')) Copy code
Method 2: array prototype. slice. call()
Array.prototype.slice.call(document.querySelectorAll('div')) Copy code
Method 3: extension operator
[...document.querySelectorAll('div')] Copy code
Method 4: using concat
Array.prototype.concat.apply([], document.querySelectorAll('div')); Copy code
04.Array.prototype.filter()
Array.prototype.filter = function(callback, thisArg) { if (this == undefined) { throw new TypeError('this is null or not undefined'); } if (typeof callback !== 'function') { throw new TypeError(callback + 'is not a function'); } const res = []; // Object passing that makes O a callback function (cast object) const O = Object(this); // >>>0 ensures that len is number and a positive integer const len = O.length >>> 0; for (let i = 0; i < len; i++) { // Check whether i is in the attribute of O (the prototype chain will be checked) if (i in O) { // Callback function call pass parameter if (callback.call(thisArg, O[i], i, O)) { res.push(O[i]); } } } return res; } Copy code
Questions about > > > 0: Explain the role of > > > 0
05.Array.prototype.map()
Array.prototype.map = function(callback, thisArg) { if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } const res = []; // Similarly const O = Object(this); const len = O.length >>> 0; for (let i = 0; i < len; i++) { if (i in O) { // Call the callback function and pass in a new array res[i] = callback.call(thisArg, O[i], i, this); } } return res; } Copy code
06.Array.prototype.forEach()
forEach is similar to map, except that forEach does not return a value.
Array.prototype.forEach = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined'); } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); } const O = Object(this); const len = O.length >>> 0; let k = 0; while (k < len) { if (k in O) { callback.call(thisArg, O[k], k, O); } k++; } } Copy code
07.Array.prototype.reduce()
Array.prototype.reduce = function(callback, initialValue) { if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callbackfn + ' is not a function'); } const O = Object(this); const len = this.length >>> 0; let accumulator = initialValue; let k = 0; // If the second parameter is undefined // The first valid value of the array is used as the initial value of the accumulator if (accumulator === undefined) { while (k < len && !(k in O)) { k++; } // TypeError if the initial value of the accumulator has not been found beyond the bounds of the array if (k >= len) { throw new TypeError('Reduce of empty array with no initial value'); } accumulator = O[k++]; } while (k < len) { if (k in O) { accumulator = callback.call(undefined, accumulator, O[k], k, O); } k++; } return accumulator; } Copy code
08.Function.prototype.apply()
The first parameter is bound this, which defaults to window, and the second parameter is array or class array
Function.prototype.apply = function(context = window, args) { if (typeof this !== 'function') { throw new TypeError('Type Error'); } const fn = Symbol('fn'); context[fn] = this; const res = context[fn](...args); delete context[fn]; return res; } Copy code
09.Function.prototype.call
The only difference from call is that the call() method accepts a list of parameters
Function.prototype.call = function(context = window, ...args) { if (typeof this !== 'function') { throw new TypeError('Type Error'); } const fn = Symbol('fn'); context[fn] = this; const res = context[fn](...args); delete context[fn]; return res; } Copy code
10.Function.prototype.bind
Function.prototype.bind = function(context, ...args) { if (typeof this !== 'function') { throw new Error("Type Error"); } // Save the value of this var self = this; return function F() { // Consider the case of new if(this instanceof F) { return new self(...args, ...arguments) } return self.apply(context, [...args, ...arguments]) } } Copy code
11.debounce (anti shake)
After triggering the high-frequency time, the function will only be executed once in n seconds. If the high-frequency time is triggered again in n seconds, the time will be recalculated.
const debounce = (fn, time) => { let timeout = null; return function() { clearTimeout(timeout) timeout = setTimeout(() => { fn.apply(this, arguments); }, time); } }; Copy code
Anti shake is often applied to search input by users to save request resources. When the window triggers the resize event, anti shake is triggered only once.
12.throttle
High frequency time trigger, but it will only be executed once in n seconds, so throttling will dilute the execution frequency of the function.
const throttle = (fn, time) => { let flag = true; return function() { if (!flag) return; flag = false; setTimeout(() => { fn.apply(this, arguments); flag = true; }, time); } } Copy code
Throttling is often used to trigger and monitor scrolling events by clicking the mouse continuously.
13. Function coritization
It refers to changing a function that accepts multiple parameters into a fixed form that accepts a parameter and returns a function, which is convenient for calling again, such as f(1)(2)
Classic interview questions: add(1)(2)(3)(4)=10 add(1)(1,2,3)(2)=9;
function add() { const _args = [...arguments]; function fn() { _args.push(...arguments); return fn; } fn.toString = function() { return _args.reduce((sum, cur) => sum + cur); } return fn; } Copy code
14. Simulate new operation
3 steps:
- In ctor Prototype creates an object for the prototype.
- Execute the constructor and bind this to the newly created object.
- Judge whether the result returned by the constructor execution is a reference data type. If so, return the result of the constructor execution. Otherwise, return the created object.
function newOperator(ctor, ...args) { if (typeof ctor !== 'function') { throw new TypeError('Type Error'); } const obj = Object.create(ctor.prototype); const res = ctor.apply(obj, args); const isObject = typeof res === 'object' && res !== null; const isFunction = typeof res === 'function'; return isObject || isFunction ? res : obj; } Copy code
15.instanceof
The instanceof operator is used to detect whether the prototype attribute of the constructor appears on the prototype chain of an instance object.
const myInstanceof = (left, right) => { // All basic data types return false if (typeof left !== 'object' || left === null) return false; let proto = Object.getPrototypeOf(left); while (true) { if (proto === null) return false; if (proto === right.prototype) return true; proto = Object.getPrototypeOf(proto); } } Copy code
16. Prototype inheritance
Only parasitic combination inheritance is written here. There are several evolved inheritance in the middle, but they all have some defects
function Parent() { this.name = 'parent'; } function Child() { Parent.call(this); this.type = 'children'; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; Copy code
17.Object.is
Object.is mainly solves these two problems:
+0 === -0 // true NaN === NaN // false Copy code const is= (x, y) => { if (x === y) { // +0 and - 0 should not be equal return x !== 0 || y !== 0 || 1/x === 1/y; } else { return x !== x && y !== y; } } Copy code
18.Object.assign
Object. The assign () method is used to copy the values of all enumerable properties from one or more source objects to the target object. It will return the target object (note that this operation is a shallow copy)
Object.defineProperty(Object, 'assign', { value: function(target, ...args) { if (target == null) { return new TypeError('Cannot convert undefined or null to object'); } // The target object needs to be a unified reference data type. If it is not, it will be automatically converted const to = Object(target); for (let i = 0; i < args.length; i++) { // Each source object const nextSource = args[i]; if (nextSource !== null) { // Use for In and hasOwnProperty double judge to ensure that only their own properties and methods (excluding inherited ones) are obtained for (const nextKey in nextSource) { if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, // countless enumerable: false, writable: true, configurable: true, }) Copy code
19. Deep copy
Full version of recursion (taking into account the Symbol attribute):
const cloneDeep1 = (target, hash = new WeakMap()) => { // For incoming parameter processing if (typeof target !== 'object' || target === null) { return target; } // There is a direct return in the hash table if (hash.has(target)) return hash.get(target); const cloneTarget = Array.isArray(target) ? [] : {}; hash.set(target, cloneTarget); // For Symbol Properties const symKeys = Object.getOwnPropertySymbols(target); if (symKeys.length) { symKeys.forEach(symKey => { if (typeof target[symKey] === 'object' && target[symKey] !== null) { cloneTarget[symKey] = cloneDeep1(target[symKey]); } else { cloneTarget[symKey] = target[symKey]; } }) } for (const i in target) { if (Object.prototype.hasOwnProperty.call(target, i)) { cloneTarget[i] = typeof target[i] === 'object' && target[i] !== null ? cloneDeep1(target[i], hash) : target[i]; } } return cloneTarget; } Copy code
20.Promise
Implementation idea: Promise source code implementation
// Simulation implementation Promise // Promise uses three means to solve the problem: // 1. Delayed binding of callback function // 2. Return value // 3. Error bubbling // Define three states const PENDING = 'PENDING'; // have in hand const FULFILLED = 'FULFILLED'; // Succeeded const REJECTED = 'REJECTED'; // Failed class Promise { constructor(exector) { // Initialization status this.status = PENDING; // Put the success and failure results on this to facilitate access by then and catch this.value = undefined; this.reason = undefined; // Successful callback function queue this.onFulfilledCallbacks = []; // Failed callback function queue this.onRejectedCallbacks = []; const resolve = value => { // The status can only be changed if the status is in progress if (this.status === PENDING) { this.status = FULFILLED; this.value = value; // Successful state functions are executed in sequence this.onFulfilledCallbacks.forEach(fn => fn(this.value)); } } const reject = reason => { // The status can only be changed if the status is in progress if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; // Failed state functions are executed in sequence this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } } try { // Execute executor now // Pass the internal resolve and reject into the executor, and the user can call resolve and reject exector(resolve, reject); } catch(e) { // The executor executes an error and throws out the error content reject reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function'? onRejected : reason => { throw new Error(reason instanceof Error ? reason.message : reason) } // Save this const self = this; return new Promise((resolve, reject) => { if (self.status === PENDING) { self.onFulfilledCallbacks.push(() => { // try capture error try { // Simulated micro task setTimeout(() => { const result = onFulfilled(self.value); // There are two cases: // 1. The return value of the callback function is Promise. Execute the then operation // 2. If it is not Promise, call the resolve function of the new Promise result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }); self.onRejectedCallbacks.push(() => { // The same is true below try { setTimeout(() => { const result = onRejected(self.reason); // Difference: reject result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }) } else if (self.status === FULFILLED) { try { setTimeout(() => { const result = onFulfilled(self.value); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }); } catch(e) { reject(e); } } else if (self.status === REJECTED) { try { setTimeout(() => { const result = onRejected(self.reason); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } } }); } catch(onRejected) { return this.then(null, onRejected); } static resolve(value) { if (value instanceof Promise) { // If it is a Promise instance, return directly return value; } else { // If it is not a Promise instance, a new Promise object is returned with the status of full return new Promise((resolve, reject) => resolve(value)); } } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } static all(promiseArr) { const len = promiseArr.length; const values = new Array(len); // Record the number of promise s that have been successfully executed let count = 0; return new Promise((resolve, reject) => { for (let i = 0; i < len; i++) { // Promise.resolve() to ensure that each is a promise instance Promise.resolve(promiseArr[i]).then( val => { values[i] = val; count++; // If all execution is completed, the status of return promise can be changed if (count === len) resolve(values); }, err => reject(err), ); } }) } static race(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach(p => { Promise.resolve(p).then( val => resolve(val), err => reject(err), ) }) }) } } Copy code
21.Promise.all
Promise.all supports chain calls. In essence, it returns a promise instance and changes the instance state through resolve and reject.
Promise.myAll = function(promiseArr) { return new Promise((resolve, reject) => { const ans = []; let index = 0; for (let i = 0; i < promiseArr.length; i++) { promiseArr[i] .then(res => { ans[i] = res; index++; if (index === promiseArr.length) { resolve(ans); } }) .catch(err => reject(err)); } }) } Copy code
22.Promise.race
Promise.race = function(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach(p => { // If it is not a Promise instance, it needs to be converted to a Promise instance Promise.resolve(p).then( val => resolve(val), err => reject(err), ) }) }) } Copy code
23.Promise parallelism limitation
This is the problem of implementing Promise scheduler with parallel constraints.
Detailed implementation ideas: Original question of a high-frequency interview: implementing Promise scheduler with parallel constraints
class Scheduler { constructor() { this.queue = []; this.maxCount = 2; this.runCounts = 0; } add(promiseCreator) { this.queue.push(promiseCreator); } taskStart() { for (let i = 0; i < this.maxCount; i++) { this.request(); } } request() { if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) { return; } this.runCounts++; this.queue.shift()().then(() => { this.runCounts--; this.request(); }); } } const timeout = time => new Promise(resolve => { setTimeout(resolve, time); }) const scheduler = new Scheduler(); const addTask = (time,order) => { scheduler.add(() => timeout(time).then(()=>console.log(order))) } addTask(1000, '1'); addTask(500, '2'); addTask(300, '3'); addTask(400, '4'); scheduler.taskStart() // 2 // 3 // 1 // 4 Copy code
24.JSONP
The script tag does not follow the same origin protocol and can be used for cross domain requests. The advantage is good compatibility, but it is limited to GET requests
const jsonp = ({ url, params, callbackName }) => { const generateUrl = () => { let dataSrc = ''; for (let key in params) { if (Object.prototype.hasOwnProperty.call(params, key)) { dataSrc += `${key}=${params[key]}&`; } } dataSrc += `callback=${callbackName}`; return `${url}?${dataSrc}`; } return new Promise((resolve, reject) => { const scriptEle = document.createElement('script'); scriptEle.src = generateUrl(); document.body.appendChild(scriptEle); window[callbackName] = data => { resolve(data); document.removeChild(scriptEle); } }) } Copy code
25.AJAX
const getJSON = function(url) { return new Promise((resolve, reject) => { const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp'); xhr.open('GET', url, false); xhr.setRequestHeader('Accept', 'application/json'); xhr.onreadystatechange = function() { if (xhr.readyState !== 4) return; if (xhr.status === 200 || xhr.status === 304) { resolve(xhr.responseText); } else { reject(new Error(xhr.responseText)); } } xhr.send(); }) } Copy code
26.event module
The mechanism of implementing the callback function in node. The callback function in node actually uses the observer mode internally.
Observer mode: defines a one to many dependency between objects. When the target object Subject changes, all object observers that depend on it will be notified.
function EventEmitter() { this.events = new Map(); } // Some methods to be implemented: // addListener,removeListener,once,removeAllListeners,emit // Simulation Implementation of addlistener method const wrapCallback = (fn, once = false) => ({ callback: fn, once }); EventEmitter.prototype.addListener = function(type, fn, once = false) { const hanlder = this.events.get(type); if (!hanlder) { // No type binding event this.events.set(type, wrapCallback(fn, once)); } else if (hanlder && typeof hanlder.callback === 'function') { // Currently, there is only one callback for the type event this.events.set(type, [hanlder, wrapCallback(fn, once)]); } else { // Current type events > = 2 hanlder.push(wrapCallback(fn, once)); } } // Simulate the implementation of removeListener EventEmitter.prototype.removeListener = function(type, listener) { const hanlder = this.events.get(type); if (!hanlder) return; if (!Array.isArray(this.events)) { if (hanlder.callback === listener.callback) this.events.delete(type); else return; } for (let i = 0; i < hanlder.length; i++) { const item = hanlder[i]; if (item.callback === listener.callback) { hanlder.splice(i, 1); i--; if (hanlder.length === 1) { this.events.set(type, hanlder[0]); } } } } // Simulation Implementation of once method EventEmitter.prototype.once = function(type, listener) { this.addListener(type, listener, true); } // Simulation Implementation of emit EventEmitter.prototype.emit = function(type, ...args) { const hanlder = this.events.get(type); if (!hanlder) return; if (Array.isArray(hanlder)) { hanlder.forEach(item => { item.callback.apply(this, args); if (item.once) { this.removeListener(type, item); } }) } else { hanlder.callback.apply(this, args); if (hanlder.once) { this.events.delete(type); } } return true; } EventEmitter.prototype.removeAllListeners = function(type) { const hanlder = this.events.get(type); if (!hanlder) return; this.events.delete(type); } Copy code
27. Picture loading
You can give img tags a unified custom attribute data src ='default Png ', when it is detected that the picture appears in the window, add the src attribute, and then the picture resource will be loaded.
function lazyload() { const imgs = document.getElementsByTagName('img'); const len = imgs.length; // The height of the viewport const viewHeight = document.documentElement.clientHeight; // Scroll bar height const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop; for (let i = 0; i < len; i++) { const offsetHeight = imgs[i].offsetTop; if (offsetHeight < viewHeight + scrollHeight) { const src = imgs[i].dataset.src; imgs[i].src = src; } } } // You can use throttling to optimize it window.addEventListener('scroll', lazyload); Copy code
28. Rolling load
The principle is to monitor page scrolling events and analyze the attribute relationship of clientHeight, scrollTop and scrollHeight.
window.addEventListener('scroll', function() { const clientHeight = document.documentElement.clientHeight; const scrollTop = document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight; if (clientHeight + scrollTop >= scrollHeight) { // Scroll to the bottom of the page if detected for subsequent operations // ... } }, false); Copy code
A Demo: Page scrolling loaded Demo
29. Rendering tens of thousands of data does not block the page
When rendering big data, reasonably use createDocumentFragment and requestAnimationFrame to cut the operation into small segments for execution.
setTimeout(() => { // Insert 100000 pieces of data const total = 100000; // Data inserted at one time const once = 20; // Number of times required to insert data const loopCount = Math.ceil(total / once); let countOfRender = 0; const ul = document.querySelector('ul'); // Method of adding data function add() { const fragment = document.createDocumentFragment(); for(let i = 0; i < once; i++) { const li = document.createElement('li'); li.innerText = Math.floor(Math.random() * total); fragment.appendChild(li); } ul.appendChild(fragment); countOfRender += 1; loop(); } function loop() { if(countOfRender < loopCount) { window.requestAnimationFrame(add); } } loop(); }, 0) Copy code
30. Print out how many HTML elements are used in the current web page
One line of code can solve:
const fn = () => { return [...new Set([...document.querySelectorAll('*')].map(el => el.tagName))].length; } Copy code
It is worth noting that the DOM operation returns an array of classes, which can only be called after being converted to an array.
31. Transform VirtualDom into real DOM structure
This is one of the core concepts of current SPA application
// vnode structure: // { // tag, // attrs, // children, // } //Virtual DOM => DOM function render(vnode, container) { container.appendChild(_render(vnode)); } function _render(vnode) { // If it is a numeric type, convert it to a string if (typeof vnode === 'number') { vnode = String(vnode); } // The string type is the text node directly if (typeof vnode === 'string') { return document.createTextNode(vnode); } // Normal DOM const dom = document.createElement(vnode.tag); if (vnode.attrs) { // traversal attributes Object.keys(vnode.attrs).forEach(key => { const value = vnode.attrs[key]; dom.setAttribute(key, value); }) } // Recursive operation of subarray vnode.children.forEach(child => render(child, dom)); return dom; } Copy code
32. String parsing
var a = { b: 123, c: '456', e: '789', } var str=`a{a.b}aa{a.c}aa {a.d}aaaa`; // => 'a123aa456aa {a.d}aaaa' Copy code
Implement the function to replace the variable in {} in str string. If the attribute does not exist, keep it as it is (such as {a.d})
It is similar to template string, but there is a difference. In fact, the principle is very different
const fn1 = (str, obj) => { let res = ''; // Flag bit, whether there is in front of the flag{ let flag = false; let start; for (let i = 0; i < str.length; i++) { if (str[i] === '{') { flag = true; start = i + 1; continue; } if (!flag) res += str[i]; else { if (str[i] === '}') { flag = false; res += match(str.slice(start, i), obj); } } } return res; } // Object matching operation const match = (str, obj) => { const keys = str.split('.').slice(1); let index = 0; let o = obj; while (index < keys.length) { const key = keys[index]; if (!o[key]) { return `{${str}}`; } else { o = o[key]; } index++; } return o; }
//Traversal properties
Object.keys(vnode.attrs).forEach(key => {
const value = vnode.attrs[key];
dom.setAttribute(key, value);
})
}
//Recursive operation of subarray
vnode.children.forEach(child => render(child, dom));
return dom;
}
Copy code
## 32. String parsing ```javascript var a = { b: 123, c: '456', e: '789', } var str=`a{a.b}aa{a.c}aa {a.d}aaaa`; // => 'a123aa456aa {a.d}aaaa' Copy code
Implement the function to replace the variable in {} in str string. If the attribute does not exist, keep it as it is (such as {a.d})
It is similar to template string, but there is a difference. In fact, the principle is very different
const fn1 = (str, obj) => { let res = ''; // Flag bit, whether there is in front of the flag{ let flag = false; let start; for (let i = 0; i < str.length; i++) { if (str[i] === '{') { flag = true; start = i + 1; continue; } if (!flag) res += str[i]; else { if (str[i] === '}') { flag = false; res += match(str.slice(start, i), obj); } } } return res; } // Object matching operation const match = (str, obj) => { const keys = str.split('.').slice(1); let index = 0; let o = obj; while (index < keys.length) { const key = keys[index]; if (!o[key]) { return `{${str}}`; } else { o = o[key]; } index++; } return o; }