One, foreword
Say job-hopping good season, gold three silver four, gold nine silver ten. Last week, I also succeeded in job-hopping and joined a new company. The road of job-hopping was bumpy, and I met meituan and Byte outsourcing posts, as well as some strange interviews.
In the last article, I promised to compile some handwritten interview questions for you. It’s coming now!
Most of the topics in this article came across when I went for an interview myself. If you have any mistakes or better answers, please leave them in the comments section
Second, the subject
1. Anti-shake and throttling
This is also a classic topic, first of all to know what is anti – shake, what is throttling.
-
Buffeting: Events will only trigger one last time over a period of time.
-
Throttling: Events that are triggered at intervals of time.
If you really don’t understand, you can go to this big guy’s Demo address to play anti-shake throttling Demo
/ / image stabilization
function debounce(fn) {
let timeout = null;
return function () {
// If the event is triggered again, clear the timer and restart the timer
clearTimeout(timeout);
timeout = setTimeout(() = > {
fn.apply(this);
}, 500);
};
}
/ / throttling
function throttle(fn) {
let flag = null; // Save a tag through a closure
return function () {
if (flag) return; // The flag is always false when the timer is not executed and is returned at the beginning
flag = setTimeout(() = > {
fn.apply(this.arguments);
// Set the flag to true after setTimeout (critical)
// Indicates that the next loop is ready to execute.
flag = null;
}, 500);
};
}
Copy the code
This question is mainly to check the understanding of anti – shake throttling bar, do not remember the reverse!
2. A regular question
Write the area code and 8-digit digits, or the area code and special number: 10010/110 separated by a short line. The area code starts with three digits.
For example 010-12345678
let reg = /^\d{3}-(\d{8}|10010|110)/g
Copy the code
This is relatively simple, familiar with the basic use of re can be done.
3. How to implement the functions of a label without using a label
// The window.open and location.href methods do this.
// Correspond to the blank and self attributes of the A tag, respectively
Copy the code
4. Do not use the loop API to delete elements at specified positions in the array (e.g., delete the third bit). Write as much as possible
A non-looping API (for filter, for example).
var arr = [1.2.3.4.5.6.7.8.9.10]
// Method 1: splice operates on an array and changes the original array
arr.splice(2.1)
// Method 2: Slice returns a new array without changing the original array
arr.slice(0.2).concat(arr.slice(3)),// Delete the element in the array and delete the element
delete arr[2]
arr.join("").replace("empty"."").split("")
Copy the code
5. Deep copy
That’s the difference between a deep copy and a shallow copy
- Shallow copy: For complex data types, shallow copy simply assigns the reference address to the new object. Changing the value of the new object changes the value of the original object.
- Deep copy: For complex data types, the address references are all new after the copy. Changing the value of the new object does not affect the value of the original object.
So the key is to deal with complex data types, and HERE I’ve written two ways, the second of which has a partial performance improvement over the first
const isObj = (val) = > typeof val === "object"&& val ! = =null;
/ / write 1
function deepClone(obj) {
// Use instanceof to determine if the variable you want to copy is an array (if not an array, then an object).
// 1. Prepare the variable you want to return (the new address).
const newObj = obj instanceof Array ? [] : {}; // Core code.
// 2. The simple data type only needs to be assigned, and if it encounters a complex data type, it goes back into deep copy until it finds a simple data type.
for (const key in obj) {
const item = obj[key];
newObj[key] = isObj(item) ? deepClone(item) : item;
}
// 3. Return the copied variables.
return newObj;
}
WeakMap has better weak reference performance and supports Symbol
function deepClone2(obj, wMap = new WeakMap(a)) {
if (isObj(obj)) {
// Check whether it is an object or an array
let target = Array.isArray(obj) ? [] : {};
// If this exists, return it directly
if (wMap.has(obj)) {
return wMap.get(obj);
}
wMap.set(obj, target);
// Iterate over the object
Reflect.ownKeys(obj).forEach((item) = > {
// If it is a complex data type, continue the recursive call
target[item] = isObj(obj[item]) ? deepClone2(obj[item], wMap) : obj[item];
});
return target;
} else {
returnobj; }}Copy the code
The main solution to this problem is recursion plus data type judgment.
For complex data types, recursively call your copy method again until it can be directly assigned to a simple data type
6. Call bind apply by hand
All call bind apply does is you can modify this point
- The difference between Call and apply is the way arguments are passed
- Bind differs in that it returns a function at the end.
// call
Function.prototype.MyCall = function (context) {
if (typeof this! = ="function") {
throw new Error('type error')}if (context === null || context === undefined) {
// This with null and undefined will automatically point to the global object (window in browsers)
context = window
} else {
// This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value
context = Object(context)
}
// Use Symbol to determine uniqueness
const fnSym = Symbol(a)// Simulate the object's this pointer
context[fnSym] = this
// Get parameters
const args = [...arguments].slice(1)
// Bind the parameters and execute the function
constresult = context[fnSym](... args)// Clear the defined this
delete context[fnSym]
// Return the result
return result
}
// call apply is a matter of changing parameters
// apply
Function.prototype.MyApply = function (context) {
if (typeof this! = ="function") {
throw new Error('type error')}if (context === null || context === undefined) {
// This with null and undefined will automatically point to the global object (window in browsers)
context = window
} else {
// This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value
context = Object(context)
}
// Use Symbol to determine uniqueness
const fnSym = Symbol(a)// Simulate the object's this pointer
context[fnSym] = this
// Get parameters
const args = [...arguments][1]
// Bind parameters and execute functions. Since apply passes in an array, it needs to be destructed
const result = arguments.length > 1? context[fnSym](... args) : context[fnSym]()// Clear the defined this
delete context[fnSym]
// Returns the result // clears the defined this
return result
}
// bind
Function.prototype.MyBind = function (context) {
if (typeof this! = ="function") {
throw new Error('type error')}if (context === null || context === undefined) {
// This with null and undefined will automatically point to the global object (window in browsers)
context = window
} else {
// This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value
context = Object(context)
}
// Simulate the object's this pointer
const self = this
// Get parameters
const args = [...arguments].slice(1)
// Finally return a function and bind this, taking into account the new call and the fact that bind can pass arguments
return function Fn(. newFnArgs) {
if (this instanceof Fn) {
return newself(... args, ... newFnArgs) }return self.apply(context, [...args, ...newFnArgs])
}
}
Copy the code
7. Handwriting implementation inheritance
I’ll only implement two methods here, parasitic combinatorial inheritance before ES6 and class inheritance after ES6.
/** * es6 before the parasitic combination inheritance */
{
function Parent(name) {
this.name = name
this.arr = [1.2.3]
}
Parent.prototype.say = () = > {
console.log('Hi');
}
function Child(name, age) {
Parent.call(this, name)
this.age = age
}
// The core code creates new Object subclasses and superclasses through object.create
// object. create: Creates a new Object, using an existing Object to provide the __proto__ of the newly created Object
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
}
/** * es6 inherits the keyword class */
{
class Parent {
constructor(name) {
this.name = name
this.arr = [1.2.3]}}class Child extends Parent {
constructor(name, age) {
super(name)
this.age = age
}
}
}
Copy the code
As a side note, ES6 Class inheritance uses parasitic combinatorial inheritance when converted to ES5 code via Babel.
There are many methods of inheritance, remember the above two basic can!
Write the new operator by hand
First we need to know what happens to an object when it’s new.
You’re essentially creating an object internally, attaching your properties and all that to that object, and then returning that object.
function myNew(fn, ... args) {
// Create a new object based on the prototype chain
let newObj = Object.create(fn.prototype)
// Add attributes to the new object and get the result of the obj function
letres = fn.call(newObj, ... args)If the result of the execution has a return value and is an object, the result of the execution is returned; otherwise, the newly created object is returned
return res && typeof res === 'object' ? res : newObj;
}
Copy the code
9. The js implementation mechanism says the result and says why
Js task execution process, the understanding of macro task and micro task
console.log("start");
setTimeout(() = > {
console.log("setTimeout1");
}, 0);
(async function foo() {
console.log("async 1");
await asyncFunction();
console.log("async2");
})().then(console.log("foo.then"));
async function asyncFunction() {
console.log("asyncFunction");
setTimeout(() = > {
console.log("setTimeout2");
}, 0);
new Promise((res) = > {
console.log("promise1");
res("promise2");
}).then(console.log);
}
console.log("end");
Copy the code
Tip:
- The script tag counts as a macro task and is executed initially
- Async await code after await will be placed in the microtask queue
Start execution:
- Log (“start”); Execute it and print it out
start
- If you go down, if you hit setTimeout1, put it
Macro task queue
- When it hits the execute function foo, print out
async 1
- Await blocked queue, first
The function that executes await
- Execute asyncFunction to print out
asyncFunction
- On the second setTimeout2,
Put it into the macro task queue
- The new Promise is executed immediately and printed out
promise1
- Call res(“promise2”), promise.then.
Put it on the microtask queue
- The asyncFunction function is finished, and the following printout async2 will be placed
Microtask queue
- Then print out the then methods that execute the function immediately
foo.then
- And finally print
end
- The queue that starts the microtask prints out the first
promise2
- And then print the second one
async2
- When the micro task is finished, execute the macro task to print the first one
setTimeout1
- Perform the second macro task print
setTimeout2
, - At this point, the function completes
Painting is not good, can understand the meaning of the line 😭. See if your ideas and answers are consistent with the process
10. How to block global Promise Reject without assigning the Reject handler
I didn’t write this one, I was thinking trycatch but it’s not global.
Later, I checked the data and found that it was a window method
// Use a Try catch to intercept only those inside a Try block
try {
new Promise((resolve, reject) = > {
reject("WTF 123");
});
} catch (e) {
console.log("e", e);
throw e;
}
// Use unhandledrejection to intercept global errors (this is correct)
window.addEventListener("unhandledrejection".(event) = > {
event && event.preventDefault();
console.log("event", event);
});
Copy the code
11. Sleep by hand
There’s only one way I can do this, and that’s what I mentioned in the JS execution flow above. Await means to block asynchronously
There’s another way to do this that I found on the Internet, which is to completely block the process
// Implement sleep with async methods of promise and await{(async() = > {console.log('start');
await sleep(3000)
console.log('end');
function sleep(timer) {
return new Promise(res= > {
setTimeout(() = >{ res() }, timer); })}}) (); }// Method two is to completely block the process to achieve sleep{(async() = > {console.log('start');
await sleep(3000)
console.log('end');
function sleep(delay) {
let t = Date.now();
while (Date.now() - t <= delay) {
continue; }}; }}) ()Copy the code
Add (1)(2) =3
With that, you can do it in a closure
I’m putting a little bit of a strain on this, how do you keep calling
// The answer to the question
const add = (num1) = > (num2) = > num2 + num1;
Add (1)(2)(3)(4)(5)....
function add(x) {
/ / store and
let sum = x;
// Function calls are added, and the function itself is returned each time
let tmp = function (y) {
sum = sum + y;
return tmp;
};
// The object's toString must be a method in which the sum is returned
tmp.toString = () = > sum
return tmp;
}
alert(add(1) (2) (3) (4) (5))
Copy the code
The key to the implementation of an infinite chain call is the object’s toString method: each object has a toString() method, which is called automatically when the object is represented as a text value or when an object is referenced as an expected string.
That is, after I call them many times, their results will be stored in the sum variable in add, and when I alert add will automatically call toString and print out sum, which is the final result
13. Completely separate data in two arrays
Is to find data that appears only once in both arrays
var a = [1.2.4], b = [1.3.8.4]
const newArr = a.concat(b).filter((item, _, arr) = > {
return arr.indexOf(item) === arr.lastIndexOf(item)
})
Copy the code
The final result is [2,3,8]. The principle is very simple: merge two arrays, and then check whether the first occurrence index of the array is the same as the last occurrence index to determine whether it is independent.
14. Determine perfect squares
It’s a question of whether a number can be squared, like if the square root of 9 is 3. 5 is wrong if you can’t square it.
var fn = function (num) {
return num ** 0.5 % 1= =0
};
Copy the code
And the idea is, you take the square root of it and you check if it’s a positive integer
15. Function execution says the result and says why
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
}
Foo.prototype.getName = function () {
console.log(3);
}
var getName = function () {
console.log(4);
}
function getName() {
console.log(5)
}
Foo.getName();
getName();
Foo().getName()
getName();
new Foo.getName();
new Foo().getName()
new new Foo().getName()
Copy the code
This problem actually depends on your understanding of the scope of the relationship
Execution Result:
-
GetName () executes a static method on the Foo function object. Print out 2
-
Execute getName(), which is the function that executes the getName variable. Print 4
- Why is the variable getName executed instead of the function getName? Thanks to
Js precompilation
- Js is precompiled before execution
Function increase
和Variable ascension
- So both functions and variables get promoted, but
Function declarations have the highest precedence
, will be promoted toThe top of the current scope
- GetName will be reassigned at the end of the execution, and the result will be
4
The function assigned to the variable
- Why is the variable getName executed instead of the function getName? Thanks to
-
Call Foo().getName() and call getName on the value returned by Foo. The Foo function is executed, which reassigns the outer getName function and returns this. This. GetName is executed. So it prints out a 1
-
Execute getName(), because of the previous step, the function is reassigned. So this is going to be the same thing as last time, which is 1 again
-
Call new foo.getName (), which is actually new to the static method getName on Foo so it’s 2. And of course if you print this inside of this function, you’ll see that it’s referring to a new object that’s coming out of new
- You can think of foo.getName () as a whole because
Here. Has a higher priority than new
- You can think of foo.getName () as a whole because
-
Call new Foo().getName(), where new Foo() returns an object and then calls getName on that object’s prototype, so the result is 3
-
Call new new Foo().getName(), this is the same as the last call, the last call did not return a value, so it does not make sense to do new. It’s also going to be 3
16. The prototype calls the interview question to state the result and say why
function Foo() {
Foo.a = function () {
console.log(1);
};
this.a = function () {
console.log(2);
};
}
Foo.prototype.a = function () {
console.log(4);
};
Function.prototype.a = function () {
console.log(3);
};
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
Copy the code
Execution Result:
-
Executing Foo (a).Foo itself does not currently have a value, will pass
__proto__
I’m looking it up, but, so the output is3
-
New instantiates Foo to generate object obj, and then calls obj.a(), but attaches an A function to the obj object inside Foo. So it’s going to be 2. If there is no internal assignment of a to this object, it will go to the prototype chain and look for the a function, and it will print 4.
-
Execute foo.a (), which Foo did in the previous step, internally assigning function A to Foo itself, so print 1 this time
17. Change array grouping to subtraction
The meaning of this topic is [5, [/ 4, 3, 2, 1]] into (5 – ((4-3-2-1)) and executed. And you can’t use eval()
Method 1: Since eval is not possible, we can use new Function 🤭
Method two: of course method one is a bit against the meaning of the question, so there is a second method
var newArr = [5The [[4.3].2.1]]
/ / 1
// Convert to a string
let newStringArr = `The ${JSON.stringify(newArr)}`
// Loop through the parentheses and minus signs
let fn = newStringArr.split("").map((el) = > {
switch (el) {
case "[":
return '('
case "]":
return ') '
case ",":
return The '-'
default:
return el
}
}).join("")
// Finally call new Function is ok!
new Function("return " + fn)()
// 2
function run(arr) {
return arr.reduce((pre, cur) = > {
let first = Array.isArray(pre) ? run(pre) : pre
let last = Array.isArray(cur) ? run(cur) : cur
return first - last
})
}
run(nweArr)
Copy the code
-
The principle of method one is very simple, turn it into a string and loop through the concatenation of parentheses and minus signs. Finally, a new Function call will do
-
Method 2 means a recursive call through reduce. If the left-hand side is not an array you can subtract the right-hand side, but if the right-hand side is an array you subtract the right-hand side first. Which is the priority of the subtraction parenthesis operation.
18. Handwriting array Flat
const flat = function (arr, deep = 1) {
// Declare a new array
let result = []
arr.forEach(item= > {
if (Array.isArray(item) && deep > 0) {
// Hierarchy decrement
dep--
// Use concat to link arrays
result = result.concat(flat(item, deep))
} else {
result.push(item)
}
})
return result
}
Copy the code
-
The idea is to generate a new array internally and iterate over the original array
-
Recurse when there is an array in the original array and deep is greater than or equal to 1. If this condition is not met, the data can be pushed directly into the new array
-
Recursion also involves reducing levels and concatenating the recursive array
-
I’m just going to return this array
19. Convert the array to tree
The topmost parent is -1, and the remaining parents are the ids of nodes in the upper layer
let arr = [
{ id: 0.name: '1'.parent: -1.childNode: []}, {id: 1.name: '1'.parent: 0.childNode: []}, {id: 99.name: 1-1 ' '.parent: 1.childNode: []}, {id: 111.name: '1-1-1'.parent: 99.childNode: []}, {id: 66.name: '1-1-2'.parent: 99.childNode: []}, {id: 1121.name: '1-1-2'.parent: 112.childNode: []}, {id: 12.name: '2'.parent: 1.childNode: []}, {id: 2.name: '2'.parent: 0.childNode: []}, {id: 21.name: '2-1'.parent: 2.childNode: []}, {id: 22.name: '2-2'.parent: 2.childNode: []}, {id: 221.name: '2-2-1'.parent: 22.childNode: []}, {id: 3.name: '3'.parent: 0.childNode: []}, {id: 31.name: '3-1'.parent: 3.childNode: []}, {id: 32.name: '3-2'.parent: 3.childNode: []}]function arrToTree(arr, parentId) {
// Determine if it is a top-level node, and return if it is. If not, determine if it is the child node you are looking for
const filterArr = arr.filter(item= > {
return parentId === undefined ? item.parent === -1 : item.parent === parentId
})
// make a recursive call to add the childNode to the childNode's childNode
filterArr.map(item= > {
item.childNode = arrToTree(arr, item.id)
return item
})
return filterArr
}
arrToTree(arr)
Copy the code
-
This problem is also done recursively, and it starts with a judgment about whether it’s a top-level node or not
-
If it is, it returns it directly. If it is not, it determines whether it is the child node to be added to the parent node
-
And then add nodes layer by layer
-
Finally, this object is returned
20. Merge arrays and sort to deduplicate
I have two arrays, and I merge them. And then de-weighting, the logic of de-weighting is that I leave the side that has more repetition.
For example, the following array has two 5’s on one side and three 5’s on the other. I need to keep three 5’s and drop two 5’s. Over and over again, the results are being sorted.
-
Array 1: [1, 100, 0, 5, 1, 5]
-
Array 2: [2, 5, 5, 5, 1, 3]
-
Final result: [0, 1, 1, 2, 3, 5, 5, 5, 100]
// Determine the maximum number of occurrences
function maxNum(item, arr) {
let num = 0;
arr.forEach(val= > {
item === val && num++
})
return num
}
function fn(arr1, arr2) {
// Use the Map data type to record times
let obj = new Map(a);// Merge the arrays and find the most times, and store them as key-value pairs into the Map data type
[...arr1, ...arr2].forEach(item= > {
let hasNum = obj.get(item)
let num = 1
if (hasNum) {
num = hasNum + 1
}
obj.set(item, num)
})
// Store the merged array
let arr = []
// Iterate over the Map data types and push the most times directly into the new array
for (const key of obj.keys()) {
if (obj.get(key) > 1) {
for (let index = 0; index < Math.max(maxNum(key, arr1), maxNum(key, arr2)); index++) {
arr.push(key)
}
} else {
arr.push(key)
}
}
// Finally sort
return arr.sort((a, b) = > a - b)
}
Copy the code
-
So the idea is, I’m going to merge two arrays
-
The Map data type is stored as key-value pairs, where the key is the data and the value is the number of occurrences of the data
-
Generates a new array to hold the merged array
-
Iterate over the Map data type, and if the number of occurrences is greater than one, find which of the two arrays has the most occurrences, and push the one that has the most occurrences into the new array. If the number of occurrences is equal to one, you just push it into the new array.
-
Finally, sort the array and return the new array.
Three, endnotes
Finally, I hope you can understand these questions and know why. Some questions are not just for you. It’s a way for you to understand what it means.
The above questions, my answer is not the optimal answer. If you have a better solution, don’t be afraid to hit the comments! I’ll add your solution to the article.
If the content of the article is helpful to you, remember the three link ~ 🎉🎉🎉 if there is an error in the article, also welcome to correct the correction!