Top JavaScript Coding Interview Questions and Answers in 2023
The most complete in history! 56 JavaScript advanced handwriting knowledge points! Help you from beginner to advanced JavaScript player
The most common test in the interview
1.Simulate the new operation
function myNew(ctor, ...args) {
const obj = Object.create(ctor.prototype); //Create an object and point to the constructor prototype
//Equivalent to
// const obj = new Object()
// obj.__proto__ = ctor.prototype;
const res = ctor.apply(obj, args); //Point the this of ctor to the newly created object
return typeof res === 'object' ? res : obj;
//If the constructor does not return data of the object type, the result of new is the object created in step 1.
}
2.instanceof keyword
function myInstanceof(left, right) {
if (typeof left !== 'object' || left === null) return false;
let proto = left.__proto__;
let prototype = right.prototype;
while (true) {
if (!proto) return false
// 3. The end of the prototype chain is null until it is not found at the end, then return false
if (proto === prototype) return true
//1. If the left and right prototypes are the same, it means that the prototype of the constructor on the right is on the prototype chain of the instance object on the left
proto = proto.__proto__
// 2. If the comparison is not successful, then continue to find the prototype on the left
}
}
3.Realize debounce function
function myDebounce(fn, delay, immediate = false) {
let timer = null;
let isimmediate = false;
const _debounce = function () {
if (timer) clearTimeout(timer);
// Determine whether to execute immediately
if (immediate && !isimmediate) {
fn.apply(this, arguments);
isimmediate = true;
} else {
//Pay attention to the arrow function, otherwise this points to window
timer = setTimeout(() => {
fn.apply(this, arguments);
// Let the next time you re-enter, you can decide whether to execute immediately according to immediate
isimmediate = false;
}, delay);
}
};
return_debounce;
}
4.Realize throttling function
function throttle(fn, delay){
let flag =true
const _throttle = function(){
if(!flag) return
flag = false
setTimeout(()=>{
fn.apply(this, arguments)
flag = true
},delay)
}
return _throttle
}
5.Implement native AJAX requests
const ajax = {
get(url, fn) {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
fn(xhr.responeText)
}
}
xhr.send()
},
post(url, data, fn) {
const xhr = new XMLHttpRequest()
xhr.open('POST', url, true)
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded')
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
fn(xhr.responeText)
}
}
xhr.send(data)
}
}
6.Implement an LRU cache function
class LRUCache {
constructor(size) {
this.size = size
this.cache = new Map()
}
get(key) {
const hasKey = this.cache.has(key)
if (hasKey) {
const val = this.cache.get(key)
this.cache.delete(key)
this.cache.set(key, val)
return val
} else {
return -1
}
}
put(key, val) {
const hasKey = this.cache.has(key)
if (hasKey) {
this.cache.delete(key)
}
this.cache.set(key, val)
if (this.cache.size > this.size) {
this.cache.delete(this.cache.keys().next().value)
}
}
}
7.Simple implementation of publish and subscribe mode
// Handwritten publish and subscribe mode EventEmitter
class EventEmitter {
constructor() {
this.events = {};
}
// implement subscription
on(type, callBack) {
if (!this.events) this.events = Object.create(null);
if (!this. events[type]) {
this.events[type] = [callBack];
} else {
this.events[type].push(callBack);
}
}
// delete subscription
off(type, callBack) {
if (!this.events[type]) return;
this.events[type] = this.events[type].filter((item) => {
return item !== callBack;
});
}
// Subscribe to the event only once
once(type, callBack) {
function fn() {
callBack();
this.off(type, fn);
}
this.on(type, fn);
}
// trigger event
emit(type, ... rest) {
this.events[type] &&
this.events[type].forEach((fn) => fn.apply(this, rest));
}
}
// Use as follows
const event = new EventEmitter();
const handle = (...rest) => {
console. log(rest);
};
event.on("click", handle);
event. emit("click", 1, 2, 3, 4);
event.off("click", handle);
event. emit("click", 1, 2);
event.once("dbClick", () => {
console.log(123456);
});
event. emit("dbClick");
event. emit("dbClick");
8.Convert DOM into a tree-structured object
<div>
<span></span>
<ul>
<li></li>
<li></li>
</ul>
</div>
Convert the upper DOM into the lower tree structure object
{
tag: 'DIV',
children: [
{ tag: 'SPAN', children: [] },
{
tag: 'UL',
children: [
{ tag: 'LI', children: [] },
{ tag: 'LI', children: [] }
]
}
]
}
Code:
function dom2tree(dom) {
const obj = {}
obj.tag = dom.tagName
obj.children = []
dom.childNodes.forEach(child => obj.children.push(dom2tree(child)))
return obj
}
9.Convert tree structure to DOM
// real rendering function
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 directly a text node
if (typeof vnode === "string") {
return document.createTextNode(vnode);
}
// normal DOM
const dom = document.createElement(vnode.tag);
if (vnode.attrs) {
// traverse properties
Object.keys(vnode.attrs).forEach((key) => {
const value = vnode.attrs[key];
dom.setAttribute(key, value);
});
}
// recursive operation on subarrays
vnode.children.forEach((child) => dom.appendChild(_render(child)));
return dom;
}
10.Calculate the number of layers of an object
const obj = {
a: { b: [1] },
c: { d: { e: { f: 1 } } }
}
console.log(loopGetLevel(obj)) // 4
Code:
function loopGetLevel(obj) {
var res = 1;
function computedLevel(obj, level) {
var level = level ? level : 0;
if (typeof obj === 'object') {
for (var key in obj) {
if (typeof obj[key] === 'object') {
computedLevel(obj[key], level + 1);
} else {
res = level + 1 > res ? level + 1 : res;
}
}
} else {
res = level > res ? level : res;
}
}
computedLevel(obj)
return res
}
11.flattening of objects
/*
raw data
const obj = {a: 1,b: [1, 2, { c: true }],c: { e: 2, f: 3 },g: null,};
transform into
{a: 1,'b[0]': 1,'b[1]': 2,'b[2].c': true,'c.e': 2,'c.f': 3, g: null}
*/
function objFlat(obj) {
let res = {};
let foo = (key, value) => {
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
foo(`${key}[${i}]`, value[i]);
}
if (value.length === 0) res[key] = [];
} else if (value instanceof Object) {
let keys = Object.keys(value);
for (let i of keys) {
foo(`${key}.${i}`, value[i]);
}
if (keys.length === 0) res[key] = {};
} else {
res[key] = value;
}
};
Object.keys(obj).forEach((v) => foo(v, obj[v]));
return res;
}
12.Implement deep copy
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
if (obj instanceof RegExp) {
return new RegExp(obj);
}
let res = obj instanceof Array ? [] : {};
for (let key in obj) {
res[key] =
typeof obj[key] == 'object' ? deepClone(obj[key]) : obj[key];
}
return res;
}
13.Handwritten Inheritance
Detailed Explanation of Prototype Chain and Handwritten Inheritance
14.handwritten quicksort
function quick(ary) {
// 4. End the recursion (if there is less than or equal to one item in ARY, it will not be processed)
if (ary. length <= 1) {
return ary;
}
// 1. Find the middle item of the array and remove it from the original array
let middleIndex = Math. floor(ary. length / 2);
let middleValue = ary.splice(middleIndex, 1)[0];
// 2. Prepare two left and right arrays, loop through each item in the remaining array, put the item smaller than the current item in the left array, otherwise put it in the right array
let aryLeft = [],
aryRight = [];
for (let i = 0; i < ary. length; i++) {
let item = ary[i];
item < middleValue ? aryLeft.push(item) : aryRight.push(item);
}
// 3. The recursive method allows the left and right arrays to continue to be processed in this way until the left and right sides are sorted (finally let the left + middle + right stitching become the final result)
return quick(aryLeft).concat(middleValue, quick(aryRight));
}
15.Implement thousands separator
function _comma(number) {
if(number<0) {
return '-'+foo(-number)
}else{
return foo(number)
}
function foo(number) {
if (number < 1000) {
return number.toString();
} else {
return (
foo(Math.floor(number / 1000)) + ',' + foo(number % 1000)
);
}
}
}
16.Extract url parameters
const _getParams = (url) => {
const querys = {};
const params =
url.indexOf('?') !== -1 ? url.split('?')[1].split('&') : [];
console.log(params);
params.forEach((item) => {
let arr = item.split('=');
querys[arr[0]] = arr[1];
});
return querys;
};
17.Please use prototype chain or class to implement chained addition, subtraction, multiplication and division
use prototype chain:
function myCalculator(num) {
this.num = num;
}
myCalculator.prototype.add = function (value) {
this.num += value;
return this;
};
myCalculator.prototype.minus = function (n) {
this.num = this.num - n;
return this;
};
myCalculator.prototype.multi = function (n) {
this.num = this.num * n;
return this;
};
myCalculator.prototype.div = function (n) {
this.num = this.num / n;
return this;
};
myCalculator.prototype.pow = function (n) {
this.num = this.num ** n;
return this;
};
let sum = new myCalculator(123);
sum.add(1).minus(4);
console.log(sum.num);//120
use Class:
class myCalculator {
constructor(value) {
this.value = value;
}
add(newValue) {
this.value = this.value + newValue;
return this;
}
reduction(newValue) {
this.value = this.value - newValue;
return this;
}
take(newValue) {
this.value = this.value * newValue;
return this;
}
division(newValue) {
this.value = this.value / newValue;
return this;
}
pow(num) {
this.value = this.value ** num;
return this;
}
}
let num = new myCalculator(1);
let afterDate = num.add(2).reduction(2).take(5).division(2);
console.log(afterDate.value);//2.5
18.CamelCase conversion
/**
{
user_info: {
user_address: {
user_province: 'Zhe Jiang',
},
user_name: 'Joe Zhang',
},
favorite_food: [
{
food_name: 'curry',
sense_of_taste: 'spicy',
},
{
food_name: 'orange',
food_type: 'sweet',
},
],
}
{
userInfo: {
userAddress: {
userProvince: 'Zhe jiang',
},
userName: 'Joe Zhang'
},
favoriteFood: [
{
foodName: 'curry',
senseOfTaste: 'spicy'
},
{
foodName: 'orange',
foodType: 'sweet'
}
]
}
**/
Underscore to camelCase:
const mapSnakeToCamel = (data) => {
if (typeof data != 'object' || !data) return data;
if (Array.isArray(data)) {
return data. map((item) => mapSnakeToCamel(item));
}
const newData = {};
for (let key in data) {
let newKey = key.replace(/_([a-z])/g, (p, m) => {
//p is all the characters matched, m is the character matched by the first parenthesis, because _ is not in the parentheses, so there is no match
return m.toUpperCase();
});
newData[newKey] = mapSnakeToCamel(data[key]);
}
return newData;
};
CamelCase to underscore:
function underline(data) {
if (typeof data != 'object' || !data) return data;9
if (Array.isArray(data)) {
return data.map((item) => underline(item));
}
const newData = {};
for (let key in data) {
let newKey = key.replace(/([A-Z])/g, (p, m) => `_${m.toLowerCase()}`);
newData[newKey] = underline(data[key]);
}
return newData;
}
19.Cyclic printing of red, yellow and green lights
/* The red light turns on once in 3s, the green light turns on once in 1s, and the yellow light turns on once in 2s; how to keep the three lights on alternately and repeatedly
*/
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
const task = (timer, light) =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (light === 'red') {
red();
} else if (light === 'green') {
green();
} else if (light === 'yellow') {
yellow();
}
resolve();
}, timer);
});
const taskRunner = async () => {
await task(3000, 'red');
await task(2000, 'green');
await task(2100, 'yellow');
taskRunner();
};
taskRunner();
Array related
Summary and handwriting of the most complete array method
Object related
Define a test object
const obj = {
name: 'fryao',
age: 18,
gender: 'female'
}
1. Object.entries
//Convert an object into an array of key-value pairs
Object.prototype.myEntries = function (obj) {
const res = []
for (let key in obj) {
obj.hasOwnProperty(key) && res.push([key, obj[key]])
}
return res
}
console.log(Object.myEntries(obj))
// [ [ 'name', 'fryao' ], [ 'age', 18 ], [ 'gender', 'female' ] ]
2. Object.fromEntries
//Convert an array of key-value pairs into an object
Object.prototype.myFromEntries = function (arr) {
const obj = {}
for (let i = 0; i < arr.length; i++) {
const [key, value] = arr[i]
obj[key] = value
}
return obj
}
3. Object.is
//Object.is(a, b), determine whether a is equal to b
Object.prototype.myIs = function (x, y) {
if (x === y) {
//When running to 1/x === 1/y, both x and y are 0, but 1/+0 = +Infinity, 1/-0 = -Infinity, are different
return x !== 0 || y !== 0 || 1 / x === 1 / y
} else {
//NaN===NaN is false, this is wrong, we do an intercept here, x !== x, then it must be NaN, y is the same
//Return true when both are NaN
return x !== x && y !== y
}
}
4. Object.assign
difficulty
- assign receives multiple objects and synthesizes multiple objects into one object
- If these objects have attributes with the same name, the later object attribute values shall prevail
-
assign returns an object, this object === the first object
//The Object.assign() method is used to copy the values of all enumerable properties from one or more source objects to a target object. It will return the target object. Object.prototype.sx_assign = function (target, ...args) { if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object') } target = Object(target) for (let nextObj of args) { for (let key in nextObj) { nextObj.hasOwnProperty(key) && (target[key] = nextObj[key]) } } return target }
Function related
1. Function.apply
Function.prototype.myApply = function (context, thisArg) {
if (typeof this !== 'function') {
throw new TypeError('not a function call');
}
const fn = Symbol('fn');
context = context || (typeof window === 'undefined' ? global : window);
thisArg = thisArg || []; //Fix the second parameter is empty
context[fn] = this;
const res = context[fn](...thisArg);
delete context[fn];
//Because there is an additional attribute for the object, it needs to be deleted. The call that comes with the system is implemented in C++ and there will be no additional attribute {fn:f}
return res;
};
2. Function.call
Function.prototype.myCall = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('Type Error');
}
const fn = Symbol('fn');
context = context?Object(context):window ;
context[fn] = this;
const res = context[fn](...args);
delete context[fn];
return res;
};
3. Function.bind
Function.prototype.myBind = function (thisArg, ...args) {
var self = this;
thisArg = thisArg || window;
return function F() {
//Because it returns a function, we can new F(), we need to judge whether it can be used as a constructor
if (this instanceof F) {
//this instanceof F is true, that is, this = new F(), the priority of new is higher than bind, so there is no need to call to change the direction of this
return new self(...args, ...arguments);
}
return self. call(thisArg, ...args, ...arguments);
//Combine the two parameters passed in
};
};
String related
1. slice
String.prototype.mySlice = function (start = 0, end) {
start = start < 0 ? this.length + start : start
end = !end && end !== 0 ? this.length : end
if (start >= end) return ''
let str = ''
for (let i = start; i < end; i++) {
str += this[i]
}
return str
}
2. substr
String.prototype.mySubstr = function (start = 0, length) {
if (length < 0) return ''
start = start < 0 ? this.length + start : start
length = (!length && length !== 0) || length > this.length - start ? this.length : start + length
let str = ''
for (let i = start; i < length; i++) {
str += this[i]
}
return str
}
3. substring
The function is roughly the same as slice,
difference:
start > end: swap values
String.prototype.mySubstring = function (start = 0, end) {
start = start < 0 ? this.length + start : start
end = !end && end !== 0 ? this.length : end
if (start >= end) [start, end] = [end, start]
let str = ''
for (let i = start; i < end; i++) {
str += this[i]
}
return str
}