After the New Year will soon come to the golden three silver four interview season, presumably many students have been eager to try, start preparing for the interview in advance, this article lists some common handwritten code implementation in the interview process for reference. What’s the point of all this hand-written code implementation, you might ask, when the community already has poly-fill or libraries to choose from, so why bother? My understanding is that in a real business development scenario, we really don’t need to use these self-written methods. A LoDash library can completely meet our needs. However, you are just an API Caller, and you often use it, but have no idea about its implementation principle, even if it is very simple to implement. So it’s very helpful to actually write out how it works. In addition, do not think that using ES6 syntax, or the latest syntax to implement ES5 or ES3 method is a ridiculous thing, on the other hand, it shows your ES6 syntax and JS development, may be a highlight for you in the interview.
(Only collecting without liking is playing a gangster!!)
(Only collecting without liking is playing a gangster!!)
Simulation of the call
- The first parameter is zero
null
orundefined
When,this
Pointing to a global objectwindow
, an auto-wrapped object that points to the original value, such asString
,Number
,Boolean
- To avoid function names and context (
context
) attribute conflict, useSymbol
Type as the unique value - Function as the context passed in (
context
) property execution - Delete this property after the function completes execution
- Return execution result
Function.prototype.myCall = function(context, ... args) {
context = (context ?? window) | |new Object(context)
const key = Symbol()
context[key] = this
constresult = context[key](... args)delete context[key]
return result
}
Copy the code
Note: The code implementation uses the new ES2020 Null identifier?? For details, please refer to Ruan Yifeng’s introduction to ECMAScript 6
To simulate the apply
- Former part and
call
The same - The second argument may not be passed, but the type must be array or array-like
Function.prototype.myApply = function(context) {
context = (context ?? window) | |new Object(context)
const key = Symbol(a)const args = arguments[1]
context[key] = this
constresult= args ? context[key](... args) : context[key]()delete context[key]
return result
}
Copy the code
Note: there are defects in code implementation, when the second parameter is class array, it is not judged (interested in how to judge class array)
To simulate the bind
- use
call / apply
The specifiedthis
- Returns a binding function
- When the returned binding function is used as a constructor
new
The context of the binding points to the instance object - To set the binding function
prototype
Of the antiderivativeprototype
Function.prototype.myBind = function(context, ... args) {
const fn = this
const bindFn = function (. newFnArgs) {
return fn.call(
this instanceof bindFn ? this: context, ... args, ... newFnArgs ) } bindFn.prototype =Object.create(fn.prototype)
return bindFn
}
Copy the code
Simulation of the new
- Create a new empty object
- the
this
Bind to an empty object - Makes an empty object
__proto__
The prototype pointing to the constructor (prototype
) - Execute the constructor to add attributes to the empty object
- Determines whether the return value of the constructor is an object. If so, use the return value of the constructor; otherwise, return the created object
const createNew = (Con, ... args) = > {
const obj = {}
Object.setPrototypeOf(obj, Con.prototype)
let result = Con.apply(obj, args)
return result instanceof Object ? result : obj
}
Copy the code
Simulation instanceof
- Iterate through the prototype chain of the left variable until the prototype of the right variable is found. If not, return
false
const myInstanceOf = (left, right) = > {
let leftValue = left.__proto__
let rightValue = right.prototype
while(true) {
if(leftValue === null) return false
if(leftValue === rightValue) return true
leftValue = leftValue.__proto__
}
}
Copy the code
Deep copy (simple version)
- Check whether the type is original. If the type is original, no copy is required
- To avoid circular references, check whether the current object exists in the storage space before copying the object. If so, return the object directly
- Create a storage space to store the mapping between the current object and the copied object
- Copy the reference type recursively until the property is of the original type
const deepClone = (target, cache = new WeakMap()) = > {
if(target === null || typeoftarget ! = ='object') {
return target
}
if(cache.get(target)) {
return target
}
const copy = Array.isArray(target) ? [] : {}
cache.set(target, copy)
Object.keys(target).forEach(key= > copy[key] = deepClone(target[key], cache))
return copy
}
Copy the code
Disadvantages: Cannot copy functions, maps, sets, re, and other types
Deep copy (Rain Creek Version)
Vuex source
- The principle is similar to the previous version
function find(list, f) {
return list.filter(f)[0]}function deepCopy(obj, cache = []) {
// just return if obj is immutable value
if (obj === null || typeofobj ! = ='object') {
return obj
}
// if obj is hit, it is in circular structure
const hit = find(cache, c => c.original === obj)
if (hit) {
return hit.copy
}
const copy = Array.isArray(obj) ? [] : {}
// put the copy into cache at first
// because we want to refer it in recursive deepCopy
cache.push({
original: obj,
copy
})
Object.keys(obj).forEach(key= > copy[key] = deepCopy(obj[key], cache))
return copy
}
Copy the code
Deep copy (complex version)
How to write a deep copy that will impress your interviewer?
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]';
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';
const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
function forEach(array, iteratee) {
let index = - 1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
}
function isObject(target) {
const type = typeof target;
returntarget ! = =null && (type === 'object' || type === 'function');
}
function getType(target) {
return Object.prototype.toString.call(target);
}
function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
}
function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe));
}
function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
}
function cloneFunction(func) {
const bodyReg = / (? <={)(.|\n)+(? =})/m;
const paramReg = / (? < = \ () + (? =\)\s+{)/;
const funcString = func.toString();
if (func.prototype) {
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const paramArr = param[0].split(', ');
return new Function(... paramArr, body[0]);
} else {
return new Function(body[0]); }}else {
return null; }}else {
return eval(funcString); }}function cloneOtherType(targe, type) {
const Ctor = targe.constructor;
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe);
case regexpTag:
return cloneReg(targe);
case symbolTag:
return cloneSymbol(targe);
case funcTag:
return cloneFunction(targe);
default:
return null; }}function clone(target, map = new WeakMap()) {
// Clone the original type
if(! isObject(target)) {return target;
}
/ / initialization
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
} else {
return cloneOtherType(target, type);
}
// Prevent circular references
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
/ / clone set
if (type === setTag) {
target.forEach(value= > {
cloneTarget.add(clone(value, map));
});
return cloneTarget;
}
/ / clone map
if (type === mapTag) {
target.forEach((value, key) = > {
cloneTarget.set(key, clone(value, map));
});
return cloneTarget;
}
// Clone objects and arrays
const keys = type === arrayTag ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
}
Copy the code
Deep copy (High Performance version)
Headline Interviewer: Do you know how to implement a high-performance version of deep copy?
const MY_IMMER = Symbol('my-immer1')
const isPlainObject = value= > {
if (
!value ||
typeofvalue ! = ='object'|| {}.toString.call(value) ! ='[object Object]'
) {
return false
}
var proto = Object.getPrototypeOf(value)
if (proto === null) {
return true
}
var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor
return (
typeof Ctor == 'function' &&
Ctor instanceof Ctor &&
Function.prototype.toString.call(Ctor) ===
Function.prototype.toString.call(Object))}const isProxy = value= >!!!!! value && !! value[MY_IMMER]function produce(baseState, fn) {
const proxies = new Map(a)const copies = new Map(a)const objectTraps = {
get(target, key) {
if (key === MY_IMMER) return target
const data = copies.get(target) || target
return getProxy(data[key])
},
set(target, key, val) {
const copy = getCopy(target)
const newValue = getProxy(val)
// This judgment is used to get the proxy target
// Copy [key] = newValue
copy[key] = isProxy(newValue) ? newValue[MY_IMMER] : newValue
return true}}const getProxy = data= > {
if (isProxy(data)) {
return data
}
if (isPlainObject(data) || Array.isArray(data)) {
if (proxies.has(data)) {
return proxies.get(data)
}
const proxy = new Proxy(data, objectTraps)
proxies.set(data, proxy)
return proxy
}
return data
}
const getCopy = data= > {
if (copies.has(data)) {
return copies.get(data)
}
const copy = Array.isArray(data) ? data.slice() : { ... data } copies.set(data, copy)return copy
}
const isChange = data= > {
if (proxies.has(data) || copies.has(data)) return true
}
const finalize = data= > {
if (isPlainObject(data) || Array.isArray(data)) {
if(! isChange(data)) {return data
}
const copy = getCopy(data)
Object.keys(copy).forEach(key= > {
copy[key] = finalize(copy[key])
})
return copy
}
return data
}
const proxy = getProxy(baseState)
fn(proxy)
return finalize(baseState)
}
Copy the code
Function image stabilization
Function stabilization executes the callback n seconds after the event is triggered. If it is triggered again within n seconds, the timer is reset. Function stabilization is mostly used in input fields
- Arrow function
this
Inheriting from the parent context, this points to the target element that triggered the event - The event is triggered when incoming
event
object - The incoming
leading
Argument to determine whether the callback function can be executed immediately, rather than after the event has stopped firing - The callback function can have a return value and needs to return the result of execution
const debounce = (fn, wait = 300, leading = true) = > {
let timerId, result
return function(. args) {
timerId && clearTimeout(timerId)
if (leading) {
if(! timerId) result = fn.apply(this, args)
timerId = setTimeout((a)= > timerId = null, wait)
} else {
timerId = setTimeout((a)= > result = fn.apply(this, args), wait)
}
return result
}
}
Copy the code
Function throttling (timer)
Function throttling refers to firing events continuously, but only executing functions once in n seconds, which is suitable for animation-related scenarios
const throttle = (fn, wait = 300) = > {
let timerId
return function(. args) {
if(! timerId) { timerId = setTimeout((a)= > {
timerId = null
return result = fn.apply(this. args) }, wait) } } }Copy the code
Function throttling (timestamp)
const throttle = (fn, wait = 300) = > {
let prev = 0
let result
return function(. args) {
let now = +new Date(a)if(now - prev > wait) {
prev = now
return result = fn.apply(this. args) } } }Copy the code
Function throttling implementation method difference
methods | Use time stamps | Use timer |
---|---|---|
When it starts to trigger | immediately | N Seconds later |
After triggering is stopped | No longer execute events | Continue to execute an event |
Function throttling (double Sword combination edition)
const throttle = (fn, wait = 300, {
// Parameter destruct assignment
leading = true,
trailing = true,} = {}) => {let prev = 0
let timerId
const later = function(args) {
timerId && clearTimeout(timerId)
timerId = setTimeout((a)= > {
timerId = null
fn.apply(this, args)
}, wait)
}
return function (. args) {
let now = +new Date(a)if(! leading)return later(args)
if(now - prev > wait) {
fn.apply(this, args)
prev = now
} else if(trailing) {
later(args)
}
}
}
Copy the code
Leading: false Disables the first execution
Trailing: false disables the stop-triggered callback
Note: Leading: false and trailing: false cannot be set at the same time.
Array to heavy
const uniqBy = (arr, key) = > {
return [...new Map(arr.map(item= > [item[key], item])).values()]
}
const singers = [
{ id: 1.name: 'Leslie Cheung' },
{ id: 1.name: 'Leslie Cheung' },
{ id: 2.name: 'Eason Chan'},]console.log(uniqBy(singers, 'id'))
/ / /
// { id: 1, name: 'Leslie Cheung' },
// { id: 2, name: 'Eason Chan' },
/ /]
Copy the code
The principle is that keys using Map cannot be repeated
Array flattening (Tips)
Use toString to turn the array into a comma-separated string, iterating through the array to change each item back to its original type. Disadvantages: Array elements must be of type Number, String is converted to Number
const str = [0.1[2[3.4]]].toString()
// '0, 1, 2, 3, 4'
const arr = str.split(', ')
/ / [' 0 ', '1', '2', '3', '4']
const newArr = arr.map(item= > +item)
// [0, 1, 2, 3, 4]
const flatten = (arr) = > arr.toString().split(', ').map(item= > +item)
Copy the code
Array flattening
Reduce + recursion
const flatten = (arr, deep = 1) = > {
return arr.reduce((cur, next) = > {
return Array.isArray(next) && deep > 1 ?
[...cur, ...flatten(next, deep - 1)] :
[...cur, next]
},[])
}
const arr = [1[2], [3[4]]]
flatten(arr, 1) // [1, [2], [3, [4]]
flatten(arr, 2) [1,2, [3, 4]]
flatten(arr, 3) // [1,2, 3, 4]
Copy the code
The function is currified
const currying = fn= >
_curry = (. args) = >args.length >= fn.length ? fn(... args) :(. newArgs) = >_curry(... args, ... newArgs)Copy the code
The idea is to use closures to store the parameters passed in, and when the number of parameters passed in is sufficient, the function is executed
Publish and subscribe to EventEmitter
class EventEmitter { #subs = {} emit(event, ... args) { if (this.#subs[event] && this.#subs[event].length) { this.#subs[event].forEach(cb => cb(... args)) } } on(event, cb) { (this.#subs[event] || (this.#subs[event] = [])).push(cb) } off(event, offCb) { if (offCb) { if (this.#subs[event] && this.#subs[event].length) this.#subs[event] = this.#subs[event].filter(cb => cb ! == offCb) } else { this.#subs[event] = [] } } }Copy the code
Subs is a private attribute of EventEmitter. It registers events on, unregisters events off, and emits emitted events
Parasitic combinatorial inheritance
function Super(foo) {
this.foo = foo
}
Super.prototype.printFoo = function() {
console.log(this.foo)
}
function Sub(bar) {
this.bar = bar
Super.call(this)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Copy the code
ES6 version of inheritance
class Super {
constructor(foo) {
this.foo = foo
}
printFoo() {
console.log(this.foo)
}
}
class Sub extends Super {
constructor(foo, bar) {
super(foo)
this.bar = bar
}
}
Copy the code
ES5 inheritance essentially creates instance objects of the subclass and then adds methods of the superclass to this. ES6 inheritance creates instance objects of the parent class first (so the super method must be called first, and then the constructor of the subclass must be used to modify this
reference
Js Basics – Interviewers want to know how well you understand call,apply,bind? [No Regrets series]
Further currification of applications of higher-order functions
JavaScript topics follow the science of underscore
How to write a deep copy that will impress your interviewer?
Headline Interviewer: Do you know how to implement a high-performance version of deep copy?
My blog is synchronized to tencent cloud + community, invite everyone to come together: cloud.tencent.com/developer/s…