JavaScript Written Section
Click to follow this official account to get the latest updates, and you can get the “Front Interview Manual” and the most standard resume template.
Implement the anti-skid function (debounce)
The mechanism of the anti-shake function is that the callback is performed n seconds after the event is triggered. If the event is triggered again within n seconds, the timer is re-timed.
The difference with the throttling function can be directly seen in this animation implementation.
Handwritten simplified version:
// Anti-shaking function
const debounce = (fn, delay) = > {
let timer = null;
return (. args) = > {
clearTimeout(timer);
timer = setTimeout((a)= > {
fn.apply(this, args);
}, delay);
};
};
Copy the code
Application scenario:
- Button submit scenario: Prevent multiple button submissions and execute only the last one
- Server validation scenario: Form validation requires server cooperation, only the last time of a series of input events are performed, and search for associative words is similar
For survival, use lodash. Debounce
Implement throttling functions
Anti – shaking function principle: in a unit of time, can only trigger a function. If this function fires multiple times per unit time, only one will take effect.
// Write a simplified version
// Throttling function
const throttle = (fn, delay = 500) = > {
let flag = true;
return (. args) = > {
if(! flag)return;
flag = false;
setTimeout((a)= > {
fn.apply(this, args);
flag = true;
}, delay);
};
};
Copy the code
Application scenario:
- Drag and drop scenario: Only once in a fixed time to prevent uHF trigger position changes
- Zoom scenario: Monitor the browser resize
- Animated scenes: Avoid triggering animations multiple times in a short period of time to cause performance problems
DeepClone
Simple version:
const newObj = JSON.parse(JSON.stringify(oldObj));
Copy the code
Limitations:
-
He cannot clone special objects such as functions, regexps, etc
-
Discard constructor for objects; all constructors will point to objects
-
Object with a circular reference
Interview version:
/** * deep Clone * @param {[type]} Parent Object Object to be cloned * @return {[type]} Deep clone object */
const clone = parent= > {
// Determine the type
const isType = (obj, type) = > {
if (typeofobj ! = ="object") return false;
const typeString = Object.prototype.toString.call(obj);
let flag;
switch (type) {
case "Array":
flag = typeString === "[object Array]";
break;
case "Date":
flag = typeString === "[object Date]";
break;
case "RegExp":
flag = typeString === "[object RegExp]";
break;
default:
flag = false;
}
return flag;
};
// Process the re
const getRegExp = re= > {
var flags = "";
if (re.global) flags += "g";
if (re.ignoreCase) flags += "i";
if (re.multiline) flags += "m";
return flags;
};
// Maintain two arrays that store circular references
const parents = [];
const children = [];
const _clone = parent= > {
if (parent === null) return null;
if (typeofparent ! = ="object") return parent;
let child, proto;
if (isType(parent, "Array")) {
// Do something special to the array
child = [];
} else if (isType(parent, "RegExp")) {
// Do special treatment for regular objects
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, "Date")) {
// Do something special for the Date object
child = new Date(parent.getTime());
} else {
// Handle the object prototype
proto = Object.getPrototypeOf(parent);
// Break the prototype chain with object.create
child = Object.create(proto);
}
// Handle circular references
const index = parents.indexOf(parent);
if(index ! =- 1) {
// If the parent array has this object, it means it has been referenced before, so return this object directly
return children[index];
}
parents.push(parent);
children.push(child);
for (let i in parent) {
/ / recursion
child[i] = _clone(parent[i]);
}
return child;
};
return _clone(parent);
};
Copy the code
Limitations:
- Some special cases are not handled, such as buffers, promises, sets, and maps
- In addition, for objects that ensure there are no circular references, we can eliminate the special handling of circular references, which can be time-consuming
Detailed principle to achieve deep cloning
Implement the Event (Event bus)
Event Bus is not only the cornerstone of each module in node, but also one of the dependent means of communication of front-end components. It also involves the subscription-publish design pattern, which is a very important foundation.
Simple version:
class EventEmeitter {
constructor() {
this._events = this._events || new Map(a);// Store event/callback key-value pairs
this._maxListeners = this._maxListeners || 10; // Set the listening limit}}// Fires an event named type
EventEmeitter.prototype.emit = function(type, ... args) {
let handler;
// Retrieve the event callback function from this._events where the event key-value pair is stored
handler = this._events.get(type);
if (args.length > 0) {
handler.apply(this, args);
} else {
handler.call(this);
}
return true;
};
// Listen for events named type
EventEmeitter.prototype.addListener = function(type, fn) {
// Store the type event and the corresponding fn function in this._events
if (!this._events.get(type)) {
this._events.set(type, fn); }};Copy the code
Interview version:
class EventEmeitter {
constructor() {
this._events = this._events || new Map(a);// Store event/callback key-value pairs
this._maxListeners = this._maxListeners || 10; // Set the listening limit}}// Fires an event named type
EventEmeitter.prototype.emit = function(type, ... args) {
let handler;
// Retrieve the event callback function from this._events where the event key-value pair is stored
handler = this._events.get(type);
if (args.length > 0) {
handler.apply(this, args);
} else {
handler.call(this);
}
return true;
};
// Listen for events named type
EventEmeitter.prototype.addListener = function(type, fn) {
// Store the type event and the corresponding fn function in this._events
if (!this._events.get(type)) {
this._events.set(type, fn); }};// Fires an event named type
EventEmeitter.prototype.emit = function(type, ... args) {
let handler;
handler = this._events.get(type);
if (Array.isArray(handler)) {
// If it is an array, it means that there are more than one listener
for (let i = 0; i < handler.length; i++) {
if (args.length > 0) {
handler[i].apply(this, args);
} else {
handler[i].call(this); }}}else {
// In the case of a single function, we can trigger it directly
if (args.length > 0) {
handler.apply(this, args);
} else {
handler.call(this); }}return true;
};
// Listen for events named type
EventEmeitter.prototype.addListener = function(type, fn) {
const handler = this._events.get(type); // Get a list of functions corresponding to the event name
if(! handler) {this._events.set(type, fn);
} else if (handler && typeof handler === "function") {
// If handler is a function, there is only one listener
this._events.set(type, [handler, fn]); // We need to store multiple listeners in an array
} else {
handler.push(fn); // There are already multiple listeners, so push the function directly into the array}}; EventEmeitter.prototype.removeListener =function(type, fn) {
const handler = this._events.get(type); // Get a list of functions corresponding to the event name
// If it is a function, it is listened only once
if (handler && typeof handler === "function") {
this._events.delete(type, fn);
} else {
let postion;
// If handler is an array, it means that the handler has been listened to many times to find the corresponding function
for (let i = 0; i < handler.length; i++) {
if (handler[i] === fn) {
postion = i;
} else {
postion = - 1; }}// If a matching function is found, clear it from the array
if(postion ! = =- 1) {
// Find the corresponding position of the array and clear the callback directly
handler.splice(postion, 1);
// If only one function is cleared, then cancel the array and save it as a function
if (handler.length === 1) {
this._events.set(type, handler[0]); }}else {
return this; }}};Copy the code
Realize the specific process and ideas see the implementation event
Implement instanceOf
/ / simulation instanceof
function instance_of(L, R) {
//L represents the left expression and R represents the right expression
var O = R.prototype; // Take the display prototype of R
L = L.__proto__; // Take the implicit prototype of L
while (true) {
if (L === null) return false;
if (O === L)
Return true if O is strictly equal to L
return true; L = L.__proto__; }}Copy the code
Simulation of the new
The new operator does these things:
- It creates an entirely new object
- It executes the [[Prototype]] (that is, __proto__) link
- It makes this point to the newly created object
- Each object created with new will eventually be linked to the function’s Prototype object by [[Prototype]]
- If the function does not return the Object type Object(including Functoin, Array, Date, RegExg, Error), then the function call in the new expression returns a reference to that Object
// objectFactory(name, 'cxk', '18')
function objectFactory() {
const obj = new Object(a);const Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
const ret = Constructor.apply(obj, arguments);
return typeof ret === "object" ? ret : obj;
}
Copy the code
Implementing a call
What did Call do:
- Sets a function as a property of an object
- Execute & to remove the function
- Specify this to the function and pass the given arguments to execute the function
- If no argument is passed, the default point is window
// Simulate call bar.myCall (null);
// Implement a call method:
Function.prototype.myCall = function(context) {
// The context is not an object
context.fn = this;
let args = [];
for (let i = 1, len = arguments.length; i < len; i++) {
args.push(arguments[i]); } context.fn(... args);letresult = context.fn(... args);delete context.fn;
return result;
};
Copy the code
Specific implementation reference JavaScript in-depth call and apply simulation implementation
Implement the Apply method
The apply principle is similar to call and will not go into detail
/ / simulate the apply
Function.prototype.myapply = function(context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if(! arr) { result = context.fn(); }else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push("arr[" + i + "]");
}
result = eval("context.fn(" + args + ")");
}
delete context.fn;
return result;
};
Copy the code
To realize the bind
What does a bind implementation do
- Return a function, bound to this, passing preset arguments
- The function returned by bind can be used as a constructor. So the constructor should invalidate this, but the arguments passed in are still valid
// MDN implementation
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this! = ='function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments.1),
fToBind = this,
fNOP = function() {},
fBound = function() {
// This instanceof fBound === true specifies that the returned fBound is called as the new constructor
return fToBind.apply(this instanceof fBound
? this
: oThis,
// Get the arguments passed in the call (fBound). This is the usual way to pass arguments to a function returned by bind
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// Maintain the prototype relationship
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
// The downstream code makes fbound. prototype an instance of fNOP, therefore
// If the returned fBound is used as the constructor for new, the new generated object is passed as this. The __proto__ of the new object is an instance of fNOP
fBound.prototype = new fNOP();
return fBound;
};
}
Copy the code
For more details, go to JavaScript in-depth bind’s simulation implementation #12
Simulation Object. The create
The object.create () method creates a new Object, using an existing Object to provide the __proto__ of the newly created Object.
/ / simulation Object. The create
function create(proto) {
function F() {}
F.prototype = proto;
return new F();
}
Copy the code
Implement class inheritance
Class inheritance in a few years ago is the focus of the content, there are n kinds of inheritance methods have their advantages and disadvantages, ES6 after the popularity of less and less important, then a variety of writing a little “back to the word has four kinds of writing method” meaning, if you also want to in-depth understanding of the little Red Book can be, we only achieve one of the most ideal inheritance.
function Parent(name) {
this.parent = name
}
Parent.prototype.say = function() {
console.log(`The ${this.parent}You play basketball like Kunkun)}function Child(name, parent) {
// Bind the superclass constructor to the subclass
Parent.call(this, parent)
this.child = name
}
/** 1. If you want to change the prototype of the Parent class, you can change the prototype of the Parent class. 3. Object. Create is a copy of the Parent prototype that is completely isolated from the Parent prototype */
Child.prototype = Object.create(Parent.prototype);
Child.prototype.say = function() {
console.log(`The ${this.parent}Well, I've been practicing for two and a half yearsThe ${this.child}`);
}
// Remember to point the construction of the subclass to the subclass itself
Child.prototype.constructor = Child;
var parent = new Parent('father');
parent.say() // Father: You play basketball like Kunkun
var child = new Child('cxk'.'father');
child.say() // Good father, I am CXK for two and a half years
Copy the code
Realize the JSON parse
var json = '{"name":"cxk", "age":25}';
var obj = eval("(" + json + ")");
Copy the code
This method is dark magic and is easily attacked by XSS. There is also a new Function that is similar.
A simple tutorial on implementing a JSON parser in half an hour
Realize the Promise
I implemented A version A long time ago, and there were A lot of notes, but I couldn’t find it. I found an annotated version on the Internet, and there was no big problem visually. The specific process can be seen in this full implementation of Promise/A+, which is the most easy to read in the history
var PromisePolyfill = (function () {
// Resolve, unlike reject, attempts to expand the thenable object
function tryToResolve (value) {
if (this === value) {
// The main reason is to prevent the following situation
// let y = new Promise(res => setTimeout(res(y)))
throw TypeError('Chaining cycle detected for promise! ')}// Try to expand an object or function according to 2.32 and 2.33
// Ensure that pre-S6 polyfills can also be mixed with ES6 native Promises
if(value ! = =null &&
(typeof value === 'object' || typeof value === 'function')) {
try {
// This is where the value of the then is recorded and the try is wrapped
// The main reason is that then can be a getter, i.e
// 1.value. Then error may be reported
// 2.value. Then may cause side effects (for example, multiple executions may result in different results)
var then = value.then
// On the other hand, there is no guarantee that then will indeed call onFullfilled/onRejected as expected
// So a flag is added to prevent resolveOrReject from being called more than once
var thenAlreadyCalledOrThrow = false
if (typeof then === 'function') {
// Is thenable then try to expand
// And the state of this object does not change until the thenable state changes
then.bind(value)(
// onFullfilled
function (value2) {
if (thenAlreadyCalledOrThrow) return
thenAlreadyCalledOrThrow = true
tryToResolve.bind(this, value2)()
}.bind(this),
// onRejected
function (reason2) {
if (thenAlreadyCalledOrThrow) return
thenAlreadyCalledOrThrow = true
resolveOrReject.bind(this.'rejected', reason2)()
}.bind(this))}else {
// Has then but then is not a function and therefore is not thenable
resolveOrReject.bind(this.'resolved', value)()
}
} catch (e) {
if (thenAlreadyCalledOrThrow) return
thenAlreadyCalledOrThrow = true
resolveOrReject.bind(this.'rejected', e)()
}
} else {
// The basic type is returned directly
resolveOrReject.bind(this.'resolved', value)()
}
}
function resolveOrReject (status, data) {
if (this.status ! = ='pending') return
this.status = status
this.data = data
if (status === 'resolved') {
for (var i = 0; i < this.resolveList.length; ++i) {
this.resolveList[i]()
}
} else {
for (i = 0; i < this.rejectList.length; ++i) {
this.rejectList[i]()
}
}
}
function Promise (executor) {
if(! (this instanceof Promise)) {
throw Error('Promise can not be called without new ! ')}if (typeofexecutor ! = ='function') {
// Non-standard but consistent with Chrome Google
throw TypeError('Promise resolver ' + executor + ' is not a function')}this.status = 'pending'
this.resolveList = []
this.rejectList = []
try {
executor(tryToResolve.bind(this), resolveOrReject.bind(this.'rejected'))}catch (e) {
resolveOrReject.bind(this.'rejected', e)()
}
}
Promise.prototype.then = function (onFullfilled, onRejected) {
// Return value penetration and error penetration. Note that error penetration is thrown instead of return, otherwise
// The promise state returned by this THEN will become resolved, which means onFullfilled in the following THEN
// it will be called, but we want to call onRejected
if (typeofonFullfilled ! = ='function') {
onFullfilled = function (data) {
return data
}
}
if (typeofonRejected ! = ='function') {
onRejected = function (reason) {
throw reason
}
}
var executor = function (resolve, reject) {
setTimeout(function () {
try {
// Get the corresponding handle function to handle this.data
// This is the new Promise
var value = this.status === 'resolved'
? onFullfilled(this.data)
: onRejected(this.data)
resolve(value)
} catch (e) {
reject(e)
}
}.bind(this))}// Then accepts two functions to return a new Promise
// Then its execution is always asynchronous with onFullfilled/onRejected
if (this.status ! = ='pending') {
return new Promise(executor.bind(this))}else {
// pending
return new Promise(function (resolve, reject) {
this.resolveList.push(executor.bind(this, resolve, reject))
this.rejectList.push(executor.bind(this, resolve, reject))
}.bind(this))}}// for prmise A+ test
Promise.deferred = Promise.defer = function () {
var dfd = {}
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
// for prmise A+ test
if (typeof module! = ='undefined') {
module.exports = Promise
}
return Promise
})()
PromisePolyfill.all = function (promises) {
return new Promise((resolve, reject) = > {
const result = []
let cnt = 0
for (let i = 0; i < promises.length; ++i) {
promises[i].then(value= > {
cnt++
result[i] = value
if (cnt === promises.length) resolve(result)
}, reject)
}
})
}
PromisePolyfill.race = function (promises) {
return new Promise((resolve, reject) = > {
for (let i = 0; i < promises.length; ++i) {
promises[i].then(resolve, reject)
}
})
}
Copy the code
Parse URL Params into objects
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* Result {user: 'anonymous', id: [123, 456], // Result {user: 'anonymous', id: [123, 456], // Result {user: 'anonymous', id: [123, 456], True, // not specifying worthy key convention is true} */
Copy the code
function parseParam(url) {
const paramsStr = /. + \? (. +) $/.exec(url)[1]; / / will be? I'm going to take the strings that follow
const paramsArr = paramsStr.split('&'); // Store the string in an array, split by &
let paramsObj = {};
// Save params to an object
paramsArr.forEach(param= > {
if (/ = /.test(param)) { // Process parameters with value
let [key, val] = param.split('='); // Separate key and value
val = decodeURIComponent(val); / / decoding
val = /^\d+$/.test(val) ? parseFloat(val) : val; // Determine whether to convert to a number
if (paramsObj.hasOwnProperty(key)) { // Add a value if the object has a key
paramsObj[key] = [].concat(paramsObj[key], val);
} else { // If the object does not have this key, create the key and set the valueparamsObj[key] = val; }}else { // Process parameters without value
paramsObj[param] = true; }})return paramsObj;
}
Copy the code
Template engine implementation
let template = 'I'm {{name}}, age {{age}}, sex {{sex}}';
let data = {
name: 'name'.age: 18
}
render(template, data); // My name is name, age 18, gender undefined
Copy the code
function render(template, data) {
const reg = /\{\{(\w+)\}\}/; // The template string is regular
if (reg.test(template)) { // Check whether there is a template string in the template
const name = reg.exec(template)[1]; // Find the field of the first string in the current template
template = template.replace(reg, data[name]); // Render the first template string
return render(template, data); // Render recursively and return the rendered structure
}
return template; // If the template does not have a template string, return it directly
}
Copy the code
Convert to a hump name
var s1 = "get-element-by-id"
// Convert to getElementById
Copy the code
var f = function(s) {
return s.replace(/-\w/g.function(x) {
return x.slice(1).toUpperCase(); })}Copy the code
Finds the characters and number of characters that occur most frequently in the string
Example: abbcccDDDDD -> the most common character is d, which occurs 5 times
let str = "abcabcabcbbccccc";
let num = 0;
let char = ' ';
// Arrange them in a certain order
str = str.split(' ').sort().join(' ');
// "aaabbbbbcccccccc"
// Define the regular expression
let re = /(\w)\1+/g;
str.replace(re,($0, $1) = > {if(num < $0.length){
num = $0.length;
char = $1; }});console.log(The most 'characters are${char}Appeared,${num}Time `);
Copy the code
String lookup
Use a basic traversal implementation to determine whether string A is contained in string B and return the first occurrence (-1 if not found).
a='34'; b='1234567'; / / return 2
a='35'; b='1234567'; / / return 1
a='355'; b='12354355'; / / return 5
isContain(a,b);
Copy the code
function isContain(a, b) {
for (let i in b) {
if (a[0] === b[i]) {
let tmp = true;
for (let j in a) {
if(a[j] ! == b[~~i + ~~j]) { tmp =false; }}if (tmp) {
returni; }}}return - 1;
}
Copy the code
Implement the thousands separator
// Keep three decimal places
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); / / return '123456789'
parseToMoney(1087654.321); / / return '1087654321'
Copy the code
function parseToMoney(num) {
num = parseFloat(num.toFixed(3));
let [integer, decimal] = String.prototype.split.call(num, '. ');
integer = integer.replace(/\d(? =(\d{3})+$)/g.'$&,);
return integer + '. ' + (decimal ? decimal : ' ');
}
Copy the code
Regular expressions (using regular forward and backward declarations):
function parseToMoney(str){
// Only match the position
let re = / (? = (? ! \b)(\d{3})+$)/g;
return str.replace(re,', ');
}
Copy the code
Determine if it is a phone number
function isPhone(tel) {
var regx = /^1[34578]\d{9}$/;
return regx.test(tel);
}
Copy the code
Verify whether it is a mailbox
function isEmail(email) {
var regx = /^([a-zA-Z0-9_\-])+@([a-zA-Z0-9_\-])+(\.[a-zA-Z0-9_\-])+$/;
return regx.test(email);
}
Copy the code
Verifying identity
function isCardNo(number) {
var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
return regx.test(number);
}
Copy the code
The public,
If you want to follow the author’s latest articles and the latest document updates in real time, please follow the programmer interview officer of the public account, and the subsequent articles will be updated in the public account first.
Resume template: follow the public number reply “template” to obtain
“Front-end Interview Manual” : a surprise manual attached to this guide. Follow the official account “FED” to get it