introduce
In the front-end interview, tearing code by hand is obviously inevitable, and accounts for a large proportion of the proportion.
Generally speaking, if you write good code, even if the theoretical knowledge is not clear, you can have a good chance to pass the interview. In fact, a lot of handwriting is often behind the examination of your understanding of the relevant theory.
There are several types of programming problems:
Implementation of a component with some functionality * Other (advanced) : Implementation of the subscription publisher pattern; Object oriented programming, process oriented programming, functional programming to put the elephant in the refrigerator and so onCopy the code
The first two types account for the largest proportion. Get into the habit of brushing leetcode once a day, focusing on data structures (stacks, lists, queues, trees), dynamic programming, DFS, and BFS
This article mainly covers the second type of various emphasis handwriting.
It is recommended to master:
- Instanceof (Examining the understanding of prototype chains)
- New (Understanding the process of creating an object instance)
- Call&apply &bind
- Handwritten Promises (understanding asynchrony)
- Handwritten native Ajax (understanding of Ajax principles and HTTP requests, with emphasis on implementation of GET and POST requests)
- Event Subscription publishing
- Other: array, string API implementation, relatively low difficulty. As long as you know how to use arrays and strings in general, you can write a general idea on the spot. (PS: I think the array reduce method is difficult, so I can read it separately. Even if you are not asked to implement reduce in the interview, it is also very helpful to use it in other questions.)
No more words, just get started
1. The handwritten instanceof
Instanceof function:
Determines whether an instance is an instance of its parent or ancestor type.
Instanceof iterates through the prototype chain of the left variable until it finds the prototype of the right variable and returns false
let myInstanceof = (target,origin) = > {
while(target) {
if(target.__proto__===origin.prototype) {
return true
}
target = target.__proto__
}
return false
}
let a = [1.2.3]
console.log(myInstanceof(a,Array)); // true
console.log(myInstanceof(a,Object)); // true
Copy the code
2. Implement the map method of array
The map() method of the array returns a new array with each element in the new array corresponding to the value returned by a call to the provided function.
Usage:
const a = [1.2.3.4];
const b = array1.map(x= > x * 2);
console.log(b); // Array [2, 4, 6, 8]
Copy the code
Before implementing this, let’s look at the parameters of the map method
The map method has two parameters, one is the method fn which operates on the array element, and the other is the method this refers to (optional)
Native implementation:
/ / implementation
Array.prototype.myMap = function(fn, thisValue) {
let res = []
thisValue = thisValue||[]
let arr = this
for(let i=0; i<arr.length; i++) {
res.push(fn.call(thisValue, arr[i],i,arr)) This refers to the current item, the current index, and the current array
}
return res
}
/ / use
const a = [1.2.3];
const b = a.myMap((a,index) = > {
return a+1; })console.log(b) // Output [2, 3, 4]
Copy the code
3. Reduce implements the map method of arrays
Map method is implemented by using the built-in Reduce method of array, and the principle of Reduce is investigated
Array.prototype.myMap = function(fn,thisValue){
var res = [];
thisValue = thisValue||[];
this.reduce(function(pre,cur,index,arr){
return res.push(fn.call(thisValue,cur,index,arr));
},[]);
return res;
}
var arr = [2.3.1.5];
arr.myMap(function(item,index,arr){
console.log(item,index,arr);
})
Copy the code
4. Reduce method of handwritten array
The reduce() method takes a function as an accumulator, and each value in the array (from left to right) begins to shrink to a single value, another array-by-array method added in ES5
Parameters:
-
Callback (a function called on each item in the array, which takes four functions 🙂
-
PreviousValue (the value returned when the callback function was last called, or the initial value)
-
CurrentValue (array element currently being processed)
-
CurrentIndex (index of the array element currently being processed)
-
Array (array that calls the reduce() method)
-
-
InitialValue (optional initialValue. The value passed to previousValue as the first call to the callback function)
function reduce(arr, cb, initialValue){
var num = initValue == undefined? num = arr[0]: initValue;
var i = initValue == undefined? 1: 0
for (i; i< arr.length; i++){
num = cb(num,arr[i],i)
}
return num
}
function fn(result, currentValue, index){
return result + currentValue
}
var arr = [2.3.4.5]
var b = reduce(arr, fn,10)
var c = reduce(arr, fn)
console.log(b) / / 24
Copy the code
5. Array flattening
Array flattening converts a multidimensional array into a one-dimensional array
1. Es6 provides a new method flat(depth)
let a = [1[2.3]];
a.flat(); / / [1, 2, 3]
a.flat(1); / / [1, 2, 3]
Copy the code
There is an even easier way to make the target array a 1-dimensional array without knowing the dimensions. Depth is set to Infinity.
let a = [1[2.3[4[5]]]];
a.flat(Infinity); [1,2,3,4,5] a is a 4-dimensional array
Copy the code
2. Use cancat
function flatten(arr) {
var res = [];
for (let i = 0, length = arr.length; i < length; i++) {
if (Array.isArray(arr[i])) {
res = res.concat(flatten(arr[i])); //concat does not alter the original array
//res.push(... flatten(arr[i])); // Or use the extension operator
} else{ res.push(arr[i]); }}return res;
}
let arr1 = [1.2[3.1], [2.3.4[2.3.4]]]
flatten(arr1); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
Copy the code
6. Coriolization of functions
Juejin. Im /post/684490…
The definition of currization is to receive some parameters, return a function to receive the rest of the parameters, after receiving enough parameters, execute the original function.
When the Coriolization function receives enough parameters, it will execute the original function. How do you determine when enough parameters are reached?
There are two ideas:
-
The length property of the function is used to obtain the number of parameters of the function. The number of parameters is the required number of parameters
-
Manually specify the number of arguments required when calling the Cremation utility function
Put these two things together and implement a simple Curry function:
/** * currify *@param The antiderivative of f sub n to be currized *@param Len Specifies the number of parameters required. The default is the number of parameters of the original function */
function curry(fn,len = fn.length) {
return _curry.call(this,fn,len)
}
/** ** transfer function *@param The antiderivative of f sub n to be currized *@param Len Number of parameters required *@param Args List of received arguments */
function _curry(fn,len,... args) {
return function (. params) {
let _args = [...args,...params];
if(_args.length >= len){
return fn.apply(this,_args);
}else{
return _curry.call(this,fn,len,... _args) } } }Copy the code
Let’s verify:
let _fn = curry(function(a,b,c,d,e){
console.log(a,b,c,d,e)
});
_fn(1.2.3.4.5); / / print: 1, 2, 3, 4, 5
_fn(1) (2) (3.4.5); / / print: 1, 2, 3, 4, 5
_fn(1.2) (3.4) (5); / / print: 1, 2, 3, 4, 5
_fn(1) (2) (3) (4) (5); / / print: 1, 2, 3, 4, 5
Copy the code
Our popular library, LoDash, also provides the Curry method, and has added a very interesting placeholder feature that changes the order of incoming arguments.
For example, if we pass a placeholder, the argument passed by this call ignores the placeholder, and the position of the placeholder is filled by the argument of the next call, like this:
Take a look at the example from the official website:
Now let’s think about how to implement the function of placeholders.
For the Curry function of Lodash, the Curry function is mounted on the LoDash object, so the LoDash object is used as the default placeholder.
Our self-implemented Curry function is not itself mounted on any objects, so we use it as a default placeholder
The purpose of using placeholders is to change the order in which arguments are passed, so in the Curry implementation, you need to record whether a placeholder is used and the position of the argument represented by the placeholder each time.
Directly on the code:
/ * * *@param F sub n to be Currified *@param Length Specifies the number of parameters. The default value is parameter number *@param Holder placeholder that defaults to the current Currization function *@return {Function} The Currie function */
function curry(fn,length = fn.length,holder = curry){
return _curry.call(this,fn,length,holder,[],[])
}
/** ** transfer function *@param The antiderivative of f sub n Coriolization *@param Length Number of arguments required by the original function *@param Holder receives placeholder *@param Args List of received parameters *@param List of placeholder positions received by holders *@return {Function} The function or final result of continuing the Curryization */
function _curry(fn,length,holder,args,holders){
return function(. _args){
// Make a copy of the parameters to avoid arguments confusion caused by multiple operations on the same function
let params = args.slice();
// Make a copy of the placeholder position list to add the newly added placeholder
let _holders = holders.slice();
// Loop in parameters, append parameters or replace placeholders
_args.forEach((arg,i) = >{
// The real parameter is preceded by a placeholder. Replace the placeholder with the real parameter
if(arg ! == holder && holders.length) {let index = holders.shift();
_holders.splice(_holders.indexOf(index),1);
params[index] = arg;
}
// There is no placeholder before the real parameter appends the parameter to the parameter list
else if(arg ! == holder && ! holders.length){ params.push(arg); }// The placeholder is passed in. There was no placeholder to record the position of the placeholder
else if(arg === holder && ! holders.length){ params.push(arg); _holders.push(params.length -1);
}
// The placeholder is passed in, and the previous placeholder is deleted
else if(arg === holder && holders.length){ holders.shift(); }});// Params does not contain a placeholder in the previous length
if(params.length >= length && params.slice(0,length).every(i= >i! ==holder)){return fn.apply(this,params);
}else{
return _curry.call(this,fn,length,holder,params,_holders)
}
}
}
Copy the code
Verify:;
let fn = function(a, b, c, d, e) {
console.log([a, b, c, d, e]);
}
let _ = {}; // Define a placeholder
let _fn = curry(fn,5, _);// Corrify the function, specifying the number of arguments required, and specifying the required placeholders
_fn(1.2.3.4.5); / / print: 1, 2, 3, 4, 5
_fn(_, 2.3.4.5) (1); / / print: 1, 2, 3, 4, 5
_fn(1, _, 3.4.5) (2); / / print: 1, 2, 3, 4, 5
_fn(1, _, 3) (_,4, _) (2) (5); / / print: 1, 2, 3, 4, 5
_fn(1, _, _, 4) (_,3) (2) (5); / / print: 1, 2, 3, 4, 5
_fn(_, 2) (_, _,4) (1) (3) (5); / / print: 1, 2, 3, 4, 5
Copy the code
So far, we have fully implemented a curry function ~~
7. Shallow copy and deep copy implementation
Deep and shallow copies are only for reference data types such as Object and Array.
Shallow copy and deep copy differences:
Shallow copy: Creates a new object with an exact copy of the original object’s property values. If the property is of a primitive type, it copies the value of the primitive type. If the property is of a reference type, it copies the memory address. If one object changes the property of the reference type, it affects the other object.
Deep copy: A complete copy of an object from the heap, creating a new area of the heap for storage. This way, changing copy values does not affect the old objects
Shallow copy implementation:
Method one:
function shallowCopy(target, origin){
for(let item in origin) target[item] = origin[item];
return target;
}
Copy the code
Other methods (built-in API) :
- Object.assign
var obj={a:1.b: [1.2.3].c:function(){console.log('i am c')}}
var tar={};
Object.assign(tar,obj);
Copy the code
Of course, this method only works with object types; for arrays you can use the slice and concat methods
- Array.prototype.slice
var arr=[1.2[3.4]].var newArr=arr.slice(0);
Copy the code
- Array.prototype.concat
var arr=[1.2[3.4]].var newArr=arr.concat();
Copy the code
The same tests as above (assign object test, slice concat array test) are better understood with the concept of shallow copy and deep copy
Deep copy implementation:
Method one:
Const a = json.parse (json.stringify (b))
Method 2:
// Implement deep copy recursion
function deepCopy(newObj,oldObj){
for(var k in oldObj){
let item=oldObj[k]
Array, object, simple type?
if(item instanceof Array){
newObj[k]=[]
deepCopy(newObj[k],item)
}else if(item instanceof Object){
newObj[k]={}
deepCopy(newObj[k],item)
}else{ // Simple data type, direct assignment
newObj[k]=item
}
}
}
Copy the code
8. Hand write call, apply, bind
Write a call
Function.prototype.myCall=function(context=window){ // Function method, so write on the Fuction prototype object
if(typeof this! = ="function") {// If is not necessary and will automatically throw an error
throw new Error(It's not a function)}const obj=context||window // The ES6 method can be used here to add default values for parameters, js strict mode global scope this is undefined
obj.fn=this //this is the context of the call,this is the function, and this is the method of obj
const arg=[...arguments].slice(1) // The first object is objres=obj.fn(... arg)delete obj.fn // Not deleting will result in more and more context attributes
return res
}
Copy the code
F.call (obj,arg1)
function f(a,b){
console.log(a+b)
console.log(this.name)
}
let obj={
name:1
}
f.myCall(obj,1.2) // Otherwise this points to window
obj.greet.call({name: 'Spike'}) // This is Spike
Copy the code
Write apply(arguments[this, [parameter 1, parameter 2…..]])
Function.prototype.myApply=function(context){ // Arrow functions never have an argument object !!!!! I can't write this as an arrow function
let obj=context||window
obj.fn=this
const arg=arguments[1[]] | |// If there are parameters, the result is an array
letres=obj.fn(... arg)delete obj.fn
return res
}
function f(a,b){
console.log(a,b)
console.log(this.name)
}
let obj={
name:'Joe'
}
f.myApply(obj,[1.2]) //arguments[1]
Copy the code
Write a bind
this.value = 2
var foo = {
value: 1
};
var bar = function(name, age, school){
console.log(name) // 'An'
console.log(age) / / 22
console.log(school) // 'home college'
}
var result = bar.bind(foo, 'An') // select * from 'An';
result(22.'College at home') // This parameter is merged with the preset parameter into the bar
Copy the code
Simple version
Function.prototype.bind = function(context, ... outerArgs) {
var fn = this;
return function(. innerArgs) { // Returns a function... Rest is the argument passed in when the actual call is made
return fn.apply(context,[...outerArgs, ...innerArgs]); // Return the function that changed this,
// Merge parameters}}Copy the code
Reasons for new failure:
Ex. :
// Declare a context
let thovino = {
name: 'thovino'
}
// Declare a constructor
let eat = function (food) {
this.food = food
console.log(`The ${this.name} eat The ${this.food}`)
}
eat.prototype.sayFuncName = function () {
console.log('func name : eat')}/ / the bind
let thovinoEat = eat.bind(thovino)
let instance = new thovinoEat('orange') Orange is in thovino
console.log('instance:', instance) / / {}
Copy the code
The generated instance is an empty object
When executing the new operator, our thovinoEat function can look like this:
function thovinoEat (. innerArgs) { eat.call(thovino, ... outerArgs, ... innerArgs) }Copy the code
Thovinoeat.call (obj,… Args), where obj is the simple empty object {} created by the new operator itself, but it does not actually replace the context object thovino inside thovinoEat. This is beyond the reach of call, because instead of replacing the this pointer inside the thovinoEat function, you should be replacing the Thovino object.
In other words, we want the new operator to point this in eat to the empty object created by the operator itself. But the third step to the thovino new operator did not succeed!
New inheritable version
Function.prototype.bind = function (context, ... outerArgs) {
let that = this;
function res (. innerArgs) {
if (this instanceof res) {
// when the new operator executes
// This in the third step of the new operator refers to the simple empty object created by the new itself {}
that.call(this. outerArgs, ... innerArgs) }else {
/ / ordinary bindthat.call(context, ... outerArgs, ... innerArgs) } } res.prototype =this.prototype / /!!!!!!
return res
}
Copy the code
9. Implement new manually
New process text description:
-
Create an empty object obj;
-
Points the implicit proTO of the empty object to the constructor’s prototype.
-
Use call to redirect this
-
If no value is returned or a non-object value is returned, obj is returned as the new object; If the return value is a new object, return that object directly.
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.sayHi=function(){
console.log('Hi! I am '+this.name)
}
let p1=new Person('Joe'.18)
//// Manually implement new
function create(){
let obj={}
// Get the constructor
let fn=[].shift.call(arguments) // Arguments is not an array but an object!! This method removes the first element of the arguments array. Let arg = [].slice.call(arguments,1)
obj.__proto__=fn.prototype
let res=fn.apply(obj,arguments) // Change this to add methods and attributes to the instance
// Make sure to return an object (in case fn is not a constructor)
return typeof res==='object'? res:obj }let p2=create(Person,'bill'.19)
p2.sayHi()
Copy the code
Details:
[].shift.call(arguments) Can also be written as:let arg=[...arguments]
let fn=arg.shift() // Enables arguments to call array methods with the constructor as the first argument
obj.__proto__=fn.prototype
// Change this to add methods and attributes to the instance
let res=fn.apply(obj,arg)
Copy the code
10. Write promises by hand (often test promises.all, promise.race)
// Promise/A+ Three states specified by the specification
const STATUS = {
PENDING: 'pending'.FULFILLED: 'fulfilled'.REJECTED: 'rejected'
}
class MyPromise {
// The constructor receives an execution callback
constructor(executor) {
this._status = STATUS.PENDING // Promise initial state
this._value = undefined // then the value of the callback
this._resolveQueue = [] // The success queue triggered when resolve
this._rejectQueue = [] // reject indicates the reject queue
// Use the arrow function to fix this (resolve is triggered in executor, otherwise this cannot be found)
const resolve = value= > {
const run = () = > {
// Promise/A+ The Promise state specified by the specification can only change from pending to depressing
if (this._status === STATUS.PENDING) {
this._status = STATUS.FULFILLED // Change the state
this._value = value // Store the current value for the then callback
// Perform the resolve callback
while (this._resolveQueue.length) {
const callback = this._resolveQueue.shift()
callback(value)
}
}
}
// Encapsulate the resolve callback into a function and place it in setTimeout to implement the promise asynchronous call feature.
setTimeout(run)
}
/ / to resolve
const reject = value= > {
const run = () = > {
if (this._status === STATUS.PENDING) {
this._status = STATUS.REJECTED
this._value = value
while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift()
callback(value)
}
}
}
setTimeout(run)
}
// Execute executor immediately when new Promise() is passed in resolve and reject
executor(resolve, reject)
}
The then method receives a successful callback and a failed callback
function then(onFulfilled, onRejected) {
// According to the specification, if the argument to then is not function, it is ignored, and the value is passed down, and the chain call continues
typeofonFulfilled ! = ='function' ? onFulfilled = value= > value : null
typeofonRejected ! = ='function' ? onRejected = error= > error : null
// then returns a new promise
return new MyPromise((resolve, reject) = > {
const resolveFn = value= > {
try {
const x = onFulfilled(value)
// The class discusses the return value, if it is a Promise, then wait for the Promise state to change, otherwise resolve
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
}
}
const rejectFn = error= > {
try {
const x = onRejected(error)
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
switch (this._status) {
case STATUS.PENDING:
this._resolveQueue.push(resolveFn)
this._rejectQueue.push(rejectFn)
break;
case STATUS.FULFILLED:
resolveFn(this._value)
break;
case STATUS.REJECTED:
rejectFn(this._value)
break; }})}catch (rejectFn) {
return this.then(undefined, rejectFn)
}
/ / promise. Finally method
finally(callback) {
return this.then(value= > MyPromise.resolve(callback()).then(() = > value), error= > {
MyPromise.resolve(callback()).then(() = > error)
})
}
// Static resolve method
static resolve(value) {
return value instanceof MyPromise ? value : new MyPromise(resolve= > resolve(value))
}
// Static reject
static reject(error) {
return new MyPromise((resolve, reject) = > reject(error))
}
// Static all method
static all(promiseArr) {
let count = 0
let result = []
return new MyPromise((resolve, reject) = > {
if(! promiseArr.length) {return resolve(result)
}
promiseArr.forEach((p, i) = > {
MyPromise.resolve(p).then(value= > {
count++
result[i] = value
if (count === promiseArr.length) {
resolve(result)
}
}, error= > {
reject(error)
})
})
})
}
Static race method
static race(promiseArr) {
return new MyPromise((resolve, reject) = > {
promiseArr.forEach(p= > {
MyPromise.resolve(p).then(value= > {
resolve(value)
}, error= > {
reject(error)
})
})
})
}
}
Copy the code
11. Handwritten native AJAX
steps
-
Create an instance of XMLHttpRequest
-
Making an HTTP request
-
The server returns a string in XML format
-
JS parses the XML and updates local pages
But over the course of history, XML has been phased out in favor of JSON.
Once you know the properties and methods, follow the AJAX steps and write the simplest GET request.
Version 1.0:
myButton.addEventListener('click'.function () {
ajax()
})
function ajax() {
let xhr = new XMLHttpRequest() // instantiate to call the method
xhr.open('get'.'https://www.google.com') // Parameter 2, url. Parameter 3: Asynchronous
xhr.onreadystatechange = () = > { // This function is called whenever the readyState property changes.
if (xhr.readyState === 4) { // The current state of the XMLHttpRequest agent.
if (xhr.status >= 200 && xhr.status < 300) { //200-300 The request succeeded
let string = request.responseText
The // json.parse () method parses JSON strings to construct JavaScript values or objects described by the strings
let object = JSON.parse(string)
}
}
}
request.send() // Used to actually make HTTP requests. GET request without parameters
}
Copy the code
Promise to realize
function ajax(url) {
const p = new Promise((resolve, reject) = > {
let xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.onreadystatechange = () = > {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status <= 300) {
resolve(JSON.parse(xhr.responseText))
} else {
reject('Request error')
}
}
}
xhr.send() // Send the HPPT request
})
return p
}
let url = '/data.json'
ajax(url).then(res= > console.log(res))
.catch(reason= > console.log(reason))
Copy the code
12. Handwritten throttling anti-shake function
Function throttling and function stabilization are both used to limit the execution frequency of functions, and are a performance optimization scheme, such as those applied towindowObject resize, Scroll events, mousemove events, text input, keyup events.Copy the code
Throttling: Fires events continuously but executes the function only once in n seconds
For example :(continuous motion needs to be called, set the time interval), like dom drag, if you use shake elimination, there will be a feeling of stagnation, because only in the stop of the execution of a time, this time should use throttling, in a certain period of time for many times, it will be much smoother.
Buffering: The function can be executed only once within n seconds after the event is triggered. If the event is triggered again within N seconds, the function execution time is recalculated.
Example :(continuous trigger does not call, triggered after a period of time to call), like imitation baidu search, it should use anti-shake, when I continuously input, will not send a request; I only send a request once if I haven’t entered in a while; If you continue typing less than this time, the time will be recalculated and the request will not be sent.
The realization of anti-shake:
function debounce(fn, delay) {
if(typeoffn! = ='function') {
throw new TypeError(F sub n is not a function.)}let timer; // Maintain a timer
return function () {
var _this = this; // call debounce to execute scoped this(the object to which the original function is mounted)
var args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(_this, args); // Apply to the object that called debounce, equivalent to _this.fn(args);
}, delay);
};
}
/ / call
input1.addEventListener('keyup', debounce(() = > {
console.log(input1.value)
}), 600)
Copy the code
Throttling implementation:
function throttle(fn, delay) {
let timer;
return function () {
var _this = this;
var args = arguments;
if (timer) {
return;
}
timer = setTimeout(function () {
fn.apply(_this, args); // Args accepts arguments to the function returned from the outside
// fn.apply(_this, arguments); Note: Chrome 14 and Internet Explorer 9 still don't accept array-like objects. If an array-like object is passed in, they throw an exception.
timer = null; // Clear the timer after fn is executed after delay, and throttle triggers to enter the timer
}, delay)
}
}
div1.addEventListener('drag', throttle((e) = > {
console.log(e.offsetX, e.offsetY)
}, 100))
Copy the code
13. Hand write promises to load pictures
function getData(url) {
return new Promise((resolve, reject) = > {
$.ajax({
url,
success(data) {
resolve(data)
},
error(err) {
reject(err)
}
})
})
}
const url1 = './data1.json'
const url2 = './data2.json'
const url3 = './data3.json'
getData(url1).then(data1= > {
console.log(data1)
return getData(url2)
}).then(data2= > {
console.log(data2)
return getData(url3)
}).then(data3= >
console.log(data3)
).catch(err= >
console.error(err)
)
Copy the code
14. The function implements one number per second
(!!!!!! This question has been asked in the byte school recruitment interview these days. What is printed by var? Why is it ok to change it to let? Is there another way to do it? In my blog, I wrote the second way to write let, but I forgot it.
ES6: Implemented using the let block-level scope principle
for(let i=0; i<=10; i++){// All prints with var are 11
setTimeout(() = >{
console.log(i);
},1000*i)
}
Copy the code
Written without LET: The principle is to create a block-level scope with a function that executes immediately
for(var i = 1; i <= 10; i++){
(function (i) {
setTimeout(function () {
console.log(i);
}, 1000 * i)
})(i);
}
Copy the code
15. Create 10 tabs and pop up the corresponding serial number when clicked?
var a
for(let i=0; i<10; i++){ a=document.createElement('a')
a.innerHTML=i+'<br>'
a.addEventListener('click'.function(e){
console.log(this) // This is the current click
e.preventDefault() // If this method is called, the default event behavior will no longer fire.
For example, after executing this method, if you click on a link (the A tag), the browser will not jump to the new URL. We can use event.isDefaultPrevented() to determine whether this method has been called (on that event object).
alert(i)
})
const d=document.querySelector('div')
d.appendChild(a) //append appends an existing element to that element.
}
Copy the code
16. Implement eventBus
Implement EventBus class, with on off once trigger function, corresponding to bind event listener, unbind, execute once to remove event binding, trigger event listener. This topic surface byte and quick hand have asked, recently busy, the answer will be updated in the follow-up
class EventBus {
on(eventName, listener) {}
off(eventName, listener) {}
once(eventName, listener) {}
trigger(eventName){}}const e = new EventBus();
// fn1 fn2
e.on('e1', fn1)
e.once('e1', fn2)
e.trigger('e1') // fn1() fn2()
e.trigger('e1') // fn1()
e.off('e1', fn1)
e.trigger('e1') // null
Copy the code
Implementation:
/ / class declaration
class EventBus {
constructor() {
this.eventList = {} // Create an object collection event
}
// Publish events
$on(eventName, fn) {
// Check whether the event name has been published. Add a publication: Create and add a publication
this.eventList[eventName]
? this.eventList[eventName].push(fn)
: (this.eventList[eventName] = [fn])
}
// Subscribe to events
$emit(eventName) {
if(! eventName)throw new Error('Please pass in the event name')
// Get the subscription parameter
const data = [...arguments].slice(1)
if (this.eventList[eventName]) {
this.eventList[eventName].forEach((i) = > {
try{ i(... data)// Polling events
} catch (e) {
console.error(e + 'eventName:' + eventName) // Collect errors during execution}}}})// Execute once
$once(eventName, fn) {
const _this = this
function onceHandle() {
fn.apply(null.arguments)
_this.$off(eventName, onceHandle) // Cancel listening after the command is successfully executed
}
this.$on(eventName, onceHandle)
}
// Unsubscribe
$off(eventName, fn) {
// Unsubscribe all subscriptions if no arguments are passed
if (!arguments.length) {
return (this.eventList = {})
}
// Cancel multiple subscriptions when eventName is passed in an array
if (Array.isArray(eventName)) {
return eventName.forEach((event) = > {
this.$off(event, fn)
})
}
// Cancel all queues under the name of the event if fn is not passed
if (arguments.length === 1| |! fn) {this.eventList[eventName] = []
}
// Cancel the fn in the event name
this.eventList[eventName] = this.eventList[eventName].filter(
(f) = >f ! == fn ) } }const event = new EventBus()
let b = function (v1, v2, v3) {
console.log('b', v1, v2, v3)
}
let a = function () {
console.log('a')
}
event.$once('test', a)
event.$on('test', b)
event.$emit('test'.1.2.3.45.123)
event.$off(['test'], b)
event.$emit('test'.1.2.3.45.123)
Copy the code
Reference:
Array flattened juejin. Im /post/5c971e…
Im /post/684490…
Throttling stabilization www.jianshu.com/p/c8b86b09d…
Event subscription publishing implementation heznb.com/archives/js…
Deep shallow copy copy segmentfault.com/a/119000001…