preface
Can tear a variety of JavaScript native functions, can be said to be necessary into the factory! At the same time, the learning and implementation of JavaScript source code can also help us quickly and solidly improve their front-end programming ability.
Recently many people and I are actively preparing for the front-end interview written test, so I organized some front-end interview written test is very easy to be asked about the implementation of the native function and a variety of front-end principle, which part of the source poke here.
Implement a new operator
We first know what new does:
- Create an empty simple JavaScript object (that is {});
- Link this object (that is, set its constructor) to another object;
- Use the newly created object of step (1) as the context for this;
- If the function does not return an object, this is returned.
We know what new does, so let’s implement it, okay
function create(Con, ... args){
// Create an empty object
this.obj = {};
// Point the empty object to the constructor's prototype chain
Object.setPrototypeOf(this.obj, Con.prototype);
// obj binds to the constructor to access properties in the constructor, this.obj.con (args)
let result = Con.apply(this.obj, args);
Return if result is an object
// The new method is invalid, otherwise obj is returned
return result instanceof Object ? result : this.obj;
}
Copy the code
Implement an array.isarray
Thinking is very simple, is to use the Object. The prototype. ToString
Array.myIsArray = function(o) {
return Object.prototype.toString.call(Object(o)) === '[object Array]';
};
Copy the code
Implement an object.create () method
function create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
Copy the code
Implement an EventEmitter
Real experience, recently in bytedance interview was asked by the interviewer, asked to write a simple Event class.
class Event {
constructor () {
// Store the event data structure
// For quick lookups, use objects (dictionaries)
this._cache = {}
}
/ / binding
on(type, callback) {
// For convenience and space saving
// Place events of the same type in an array
// The array is a queue, followed by first-in, first-out
// The newly bound event is emitted first
let fns = (this._cache[type] = this._cache[type] || [])
if(fns.indexOf(callback) === - 1) {
fns.push(callback)
}
return this
}
/ / unbundling
off (type, callback) {
let fns = this._cache[type]
if(Array.isArray(fns)) {
if(callback) {
let index = fns.indexOf(callback)
if(index ! = =- 1) {
fns.splice(index, 1)}}else {
// Empty all
fns.length = 0}}return this
}
/ / triggers emit
trigger(type, data) {
let fns = this._cache[type]
if(Array.isArray(fns)) {
fns.forEach((fn) = > {
fn(data)
})
}
return this
}
// One-time binding
once(type, callback) {
let wrapFun = (a)= > {
callback.call(this);
this.off(type, wrapFun);
};
this.on(type, wrapFun);
return this; }}let e = new Event()
e.on('click'.function(){
console.log('on')})// e.trigger('click', '666')
console.log(e)
Copy the code
Implement an array.prototype.reduce
First look at the array.prototype.reduce syntax
Array.prototype.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
Copy the code
Then you can implement it:
Array.prototype.myReduce = function(callback, initialValue) {
let accumulator = initialValue ? initialValue : this[0];
for (let i = initialValue ? 0 : 1; i < this.length; i++) {
let _this = this;
accumulator = callback(accumulator, this[i], i, _this);
}
return accumulator;
};
/ / use
let arr = [1.2.3.4];
let sum = arr.myReduce((acc, val) = > {
acc += val;
return acc;
}, 5);
console.log(sum); / / 15
Copy the code
Implement a call or apply
Let’s start with a call example to see what call does:
let foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call(foo); / / 1
Copy the code
From the code’s execution, we can see that call first changes the reference to this so that the function’s this points to foo, and then causes the bar function to execute. To sum up:
- Call changes the function this to point to
- Call a function
Think about it: How can we achieve this? The code modification is as follows:
Function.prototype.myCall = function(context) {
context = context || window;
// Mount the function to the object's FN property
context.fn = this;
// Process the parameters passed in
const args = [...arguments].slice(1);
// Call this method from an object's property
constresult = context.fn(... args);// Delete this attribute
delete context.fn;
return result
};
Copy the code
Let’s look at the code above:
- The first thing we do is we do compatibility with the context parameter, we don’t pass the value, the default value of context is window;
- Then we mount the function onto the context,context.fn = this;
- Process parameters by intercepting the parameters passed into myCall, removing the first digit, and converting it to an array;
- Call context.fn, where fn’s this points to the context;
- Delete context.fn;
- Return the result.
By the way, let’s implement apply, the only difference is the processing of parameters, the code is as follows:
Function.prototype.myApply = function(context) {
context = context || window
context.fn = this
let result
// myApply takes the form of (obj,[arg1,arg2,arg3]);
// So myApply's second argument is [arg1,arg2,arg3]
// We use the extension operator to handle the way arguments are passed in
if (arguments[1]) {result = context.fn(...arguments[1])}else {
result = context.fn()
}
delete context.fn;
return result
};
Copy the code
This is a mock implementation of Call and Apply, the only difference being how parameters are handled.
Implement a function.prototype.bind
function Person(){
this.name="zs";
this.age=18;
this.gender="Male"
}
let obj={
hobby:"Reading"
}
// Bind this to obj
let changePerson = Person.bind(obj);
// Call the constructor directly, the function will manipulate the obj object, add three attributes to it;
changePerson();
// output obj
console.log(obj);
// Create a new instance with the constructor to which this points
let p = new changePerson();
// output obj
console.log(p);
Copy the code
Take a close look at the code above and see the output.
We use bind on the Person class to point its this to obj, and we get the ChangePerson function, where if we call ChangePerson directly it will change obj, Calling ChangePerson with new gives instance P, and its __proto__ points to Person, and we see that bind is broken.
We conclude that using bind changes the function to which this refers, and that if called with the new operator, bind will be invalidated.
This object is an instanceof the constructor, so we can determine if the function is called by the new operator by simply executing the this instanceof constructor inside the function to check whether the result is true. If the result is true, the function is called by the new operator.
/ / the bind
Function.prototype.mybind = function(){
// save the function
let _this = this;
// 2. Save the target object
let context = arguments[0] | |window;
// 3. Save the parameters outside the target object and convert them to arrays.
let rest = Array.prototype.slice.call(arguments.1);
// return a function to be executed
return function F(){
// 5. Convert the parameters passed twice to an array;
let rest2 = Array.prototype.slice.call(arguments)
if(this instanceof F){
// if the new operator is used, the original function is called with new and the extension operator is passed as arguments
return new_this(... rest2) }else{
Context._this (rest.concat(rest2)); // use apply to call the function saved in the first step and bind this, passing the merged array of parameters._this.apply(context,rest.concat(rest2)); }}};Copy the code
Implement a JS function kerrization
The concept of Currying is not complicated, in plain English: call a function by passing it only a few arguments and having it return a function that handles the rest of the arguments.
function progressCurrying(fn, args) {
let _this = this
let len = fn.length;
let args = args || [];
return function() {
let _args = Array.prototype.slice.call(arguments);
Array.prototype.push.apply(args, _args);
// If the number of arguments is less than the original fn.length, the recursive call continues to collect arguments
if (_args.length < len) {
return progressCurrying.call(_this, fn, _args);
}
// After parameters are collected, run fn
return fn.apply(this, _args); }}Copy the code
Debouncing and Throttling
The throttle
The anti-vibration function onScroll will trigger once at the end of the function to delay execution
function debounce(func, wait) {
let timeout;
return function() {
let context = this; // point to global
let args = arguments;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout((a)= > {
func.apply(context, args); // context.func(args)
}, wait);
};
}
/ / use
window.onscroll = debounce(function() {
console.log('debounce');
}, 1000);
Copy the code
The throttle
The throttle function onscroll is triggered every once in a while, like a water drop
function throttle(fn, delay) {
let prevTime = Date.now();
return function() {
let curTime = Date.now();
if (curTime - prevTime > delay) {
fn.apply(this.arguments); prevTime = curTime; }}; }/ / use
var throtteScroll = throttle(function() {
console.log('throtte');
}, 1000);
window.onscroll = throtteScroll;
Copy the code
Hand write a deep copy of JS
The beggar version
JSON.parse(JSON.stringfy));
Copy the code
Very simple, but the pitfalls are obvious, such as copying other reference types, copying functions, circular references, and so on.
Basic version
function clone(target){
if(typeof target === 'object') {let cloneTarget = {};
for(const key in target){
cloneTarget[key] = clone(target[key])
}
return cloneTarget;
} else {
return target
}
}
Copy the code
Writing here has helped you cope with some interviewers who are testing your recursive problem-solving skills. But obviously, there are some problems with this deep-copy function.
For a more complete deep-copy function, consider both objects and arrays, and consider circular references:
function clone(target, map = new WeakMap()) {
if(typeof target === 'object') {let cloneTarget = Array.isArray(target) ? [] : {};
if(map.get(target)) {
return target;
}
map.set(target, cloneTarget);
for(const key in target) {
cloneTarget[key] = clone(target[key], map)
}
return cloneTarget;
} else {
returntarget; }}Copy the code
Implement an instanceOf
L proto = r. prototype, not l.proto. Proto until proTO is null
// L represents the left expression, R represents the right expression
function instance_of(L, R) {
var O = R.prototype;
L = L.__proto__;
while (true) {
if (L === null) {return false;
}
// Return true if O is strictly L
if (O === L) {
return true; } L = L.__proto__; }}Copy the code
Implement a prototype chain inheritance
function myExtend(C, P) {
var F = function(){};
F.prototype = P.prototype;
C.prototype = new F();
C.prototype.constructor = C;
C.super = P.prototype;
}
Copy the code
Implement an async/await
The principle of
The idea is to use generators to split snippets of code. Then we use a function that iterates over itself, with each yield wrapped in a promise. The timing of the next step is controlled by the promise
implementation
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
// Promise the return value
return new Promise(function(resolve, reject) {
// Get an iterator instance
var gen = fn.apply(self, args);
// Go to the next step
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
}
// Throw an exception
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
}
// The first time
_next(undefined);
});
};
}
Copy the code
Implement an array.prototype.flat () function
Recently, byteDance’s front-end interview was also asked by the interviewer, asking for handwritten implementation.
Array.prototype.myFlat = function(num = 1) {
if (Array.isArray(this)) {
let arr = [];
if (!Number(num) || Number(num) < 0) {
return this;
}
this.forEach(item= > {
if(Array.isArray(item)){
let count = num
arr = arr.concat(item.myFlat(--count))
} else {
arr.push(item)
}
});
return arr;
} else {
throw tihs + ".flat is not a function"; }};Copy the code
Implement an event broker
This question also generally leads you to talk about event bubbling and event capture mechanisms
<ul id="color-list"> <li>red</li> <li>yellow</li> <li>blue</li> <li>green</li> <li>black</li> <li>white</li> </ul> <script> (function () { var color_list = document.getElementById('color-list'); color_list.addEventListener('click', showColor, true); function showColor(e) { var x = e.target; if (x.nodeName.toLowerCase() === 'li') { alert(x.innerHTML); }}}) (); </script>Copy the code
Implement a Vue bidirectional binding
Object. DefineProperty version of Vue 2.x
/ / data
const data = {
text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// Data hijacking
Object.defineProperty(data, 'text', {
// Data changes - > Modify viewset(newVal) { input.value = newVal; span.innerHTML = newVal; }});// View changes --> Data changes
input.addEventListener('keyup'.function(e) {
data.text = e.target.value;
});
Copy the code
Vue 3.x Proxy version
/ / data
const data = {
text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// Data hijacking
const handler = {
set(target, key, value) {
target[key] = value;
// Data changes - > Modify view
input.value = value;
span.innerHTML = value;
returnvalue; }};const proxy = new Proxy(data, handler);
// View changes --> Data changes
input.addEventListener('keyup'.function(e) {
proxy.text = e.target.value;
});
Copy the code
What are the advantages of using ES6 Proxy over Object.defineProperty for Vue bidirectional binding implementation?
Implement array.prototype.map ()
Take a look at the use of Reduce and Map
let new_array = arr.map(function callback(currentValue[, index[,array) {}[, thisArg])
let result = arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
Copy the code
Let’s start with a for loop:
Array.prototype.myMap = function(callback, thisArg) {
let arr = [];
for (let i = 0; i < this.length; i++) {
arr.push(callback.call(thisArg, this[i], i, this));
}
return arr;
};
Copy the code
Then reduce is used
Array.prototype.myMap2 = function(callback, thisArg) {
let result = this.reduce((accumulator, currentValue, index, array) = > {
accumulator.push(callback.call(thisArg, currentValue, index, array));
returnaccumulator; } []);return result;
};
Copy the code
conclusion
Read feel to you have the help of trouble a praise encouragement, study makes me happy!
Reference:
Rewrite manual implementation of bind function – cloud + community – Tencent cloud
All kinds of source code implementation, you want to have here
I’m Cloudy, now living in Shanghai, a young front-end siege lion, love research, love technology, love to share. Personal notes, not easy to organize, thanks for your attention, reading, like and favorites. The article has any problem welcome to point out, also welcome to communicate with all kinds of front-end problems!