Several interviews in 2021 made me knock 17 JS handwritten questions!

1. Implementation of shallow copy and deep copy

Shallow copy

// 1.. implementation
let copy1 = {...{x:1}}

// 2. Object.assign implementation
let copy2 = Object.assign({}, {x:1})

Deep copy

javascript deep copy, shallow copy and implementation method (recommended)_ Paper airplane blog - CSDN blog_ js difference between shallow copy and deep copy? Shallow copy: directly assign the reference of the original Object or original Array to the new Object, the new Array, and the new Object / Array is only a reference of the original Object. Deep copy: create a new Object and Array, and copy the "values" (all elements of the Array) of the attributes of the original Object, which are "values" rather than "references". Why use deep copy? We hope that when changing the new Array (Object), the original Array (Object) will not be changed. Generally, the deep copy method 1.JSON is used to implement function deepclone2 (obj) {let _obj = json.sthttps://blog.csdn.net/qq_32442973/article/details/118584594

2. Handwritten anti chattering throttle function  

In depth understanding of javascript anti shake and throttling paper airplane blog - CSDN blog basic concept function anti shake (debounce): the function will be executed only once within n seconds after the high-frequency event is triggered. If the high-frequency event is triggered again within n seconds, the time will be recalculated. Function throttling : high frequency events are triggered, but they will only be executed once in n seconds, so throttling will dilute the execution frequency of the function. Both function debounce and function throttling are to limit the execution frequency of the function, so as to optimize the response speed that can not keep up with the trigger frequency caused by too high function trigger frequency, resulting in delay, false death or jamming. Function debounce Implementation method: set a delayed calling method every time an event is triggered, and cancel the previous delayed calling methodhttps://blog.csdn.net/qq_32442973/article/details/118739927

3. instanceof (review the understanding of prototype chain)  

instanceof function: judge whether an instance is an instance of its parent class or ancestor type.

instanceof   In the process of searching, the prototype chain of the left variable will be traversed until the prototype chain of the right variable is found   Prototype lookup failed and returned 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

4. map method for implementing array

Array.prototype.newMap = function(fn) {
   var newArr = [];
   for(var i = 0; i<this.length; i++){
     newArr.push(fn(this[i],i,this))
   }
   return newArr;
}

5. Implement new method

function createNew() {
    let obj = {}  // 1. Create an empty object

    let constructor = [].shift.call(arguments) 
    // let [constructor,...args] = [...arguments]  

    obj.__proto__ = constructor.prototype  // 2. Link to prototype

    let result = constructor.apply(obj, arguments)  // 3. Bind this value and add methods and properties for the instance
    // let result = constructor.apply(obj, args)   

    return typeof result === 'object' ? result : obj  // 4. Return new object
}

function People(name,age) {
    this.name = name
    this.age = age
}

let peo = createNew(People,'Bob',22)
console.log(peo.name)
console.log(peo.age)

6. Implement call & apply & bind

call 

Function.prototype.myCall = function (context = window) {
  // Function, so it is written on the function prototype object
  if (typeof this !== "function") {
    // if it's not necessary, an error will be thrown automatically
    throw new Error("Not a function");
  }
  const obj = context || window; //Here, you can use the ES6 method 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 here, and this function is used as the method of obj
  const arg = [...arguments].slice(1); //The first one is obj, so delete it and turn the pseudo array into an array
  res = obj.fn(...arg);
  delete obj.fn; // Not deleting will result in more and more context attributes
  return res;
};

apply(arguments[this, [parameter 1, parameter 2...])

Function.prototype.myApply = function (context) {
  // The arrow function never has a parameter object!!!!! It cannot be written as an arrow function here
  let obj = context || window;
  obj.fn = this;
  const arg = arguments[1] || []; //If you have parameters, you get an array
  let res = obj.fn(...arg);
  delete obj.fn;
  return res;
};
function f(a, b) {
  console.log(a, b);
  console.log(this.name);
}
let obj = {
  name: "Zhang San",
};
f.myApply(obj, [1, 2]); //arguments[1]

bind

// Idea: it is similar to call, but it returns a function
Function.prototype.mybind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  let _this = this
  let arg = [...arguments].slice(1)
  return function F() {
    // Handle the case where the function uses new
    if (this instanceof F) {
      return new _this(...arg, ...arguments)
    } else {
      return _this.apply(context, arg.concat(...arguments))
    }
  }
}

More implementations: Implementation of bind method

7. Implement promise manually

// Three states specified in Promise/A + specification
const STATUS = {
 PENDING: 'pending',
 FULFILLED: 'fulfilled',
 REJECTED: 'rejected'
}

class MyPromise {
 // Constructor receives an execution callback
 constructor(executor) {
     this._status = STATUS.PENDING // Promise initial state
     this._value = undefined // The value of the then callback
     this._resolveQueue = [] // Success queue triggered when resolve
     this._rejectQueue = [] // Failure queue triggered when reject
    
 // Use the arrow function to fix this (the resolve function is triggered in the executor, otherwise this cannot be found)
 const resolve = value => {
     const run = () => {
         // The Promise status specified in Promise/A + specification can only be triggered from pending to fulfilled
         if (this._status === STATUS.PENDING) {
             this._status = STATUS.FULFILLED // Change status
             this._value = value // Stores the current value for the then callback
            
             // Execute resolve callback
             while (this._resolveQueue.length) {
                 const callback = this._resolveQueue.shift()
                 callback(value)
             }
         }
     }
     //Encapsulate the resolve callback operation into a function and put it into setTimeout to realize the feature of promise asynchronous call (micro task in the specification, macro task here)
     setTimeout(run)
 }

 // Same as 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 the executor immediately upon new Promise() and pass in resolve and reject
     executor(resolve, reject)
 }

 // then method, which receives a successful callback and a failed callback
 function then(onFulfilled, onRejected) {
  // According to the specification, if the parameter of then is not function, ignore it, let the value continue to pass down, and the chain call continues to execute down
  typeof onFulfilled !== 'function' ? onFulfilled = value => value : null
  typeof onRejected !== 'function' ? onRejected = error => error : null

  // then returns a new promise
  return new MyPromise((resolve, reject) => {
    const resolveFn = value => {
      try {
        const x = onFulfilled(value)
        // Discuss the return value by category. If it is Promise, wait for the Promise status to change, otherwise resolve directly
        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 method
 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)
          })
        })
      })
    }
}

8. Handwritten native AJAX

step

  1. Create XMLHttpRequest instance

  2. Issue HTTP request

  3. The server returns a string in XML format

  4. JS parses XML and updates local pages

    However, with the progress of history, XML has been eliminated and replaced by JSON.

After knowing the properties and methods, write the simplest GET request according to the AJAX steps.

myButton.addEventListener("click", function () {
  ajax();
});

function ajax() {
  let xhr = new XMLHttpRequest(); //Instantiate to call the method
  xhr.open("get", "https://www.baidu.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 request succeeded
        let string = request.responseText;
        //The JSON.parse() method is used to parse JSON strings and construct JavaScript values or objects described by strings
        let object = JSON.parse(string);
      }
    }
  };
  request.send(); //Used to actually issue HTTP requests. GET requests without parameters
}

promise based implementation:

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 hppt request
  });
  return p;
}
let url = "/data.json";
ajax(url)
  .then((res) => console.log(res))
  .catch((reason) => console.log(reason));

9. Implementation of Coriolis function

Definition of Coriolis function: convert multi parameter function into single parameter form.
Principle of implementation of Coriolis function: using the closure principle, a non destruction scope can be formed during execution, and then the contents that need to be preprocessed are stored in the non destruction scope, and a least parameter function is returned.

There are many ways to ask questions. For more comprehensive ones, see this article: JS function coritization

10. Implement a two-way data binding

let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// Data hijacking
Object.defineProperty(obj, 'text', {
  configurable: true,
  enumerable: true,
  get() {
    console.log('Got the data')
  },
  set(newVal) {
    console.log('The data is updated')
    input.value = newVal
    span.innerHTML = newVal
  }
})
// input monitoring 
input.addEventListener('keyup', function(e) {
  obj.text = e.target.value
})

11. rem basic settings

// Execute in advance. The initialize resize event will not be executed
setRem()
// original configuration
function setRem () {
  let doc = document.documentElement
  let width = doc.getBoundingClientRect().width
  let rem = width / 75
  doc.style.fontSize = rem + 'px'
}
// Monitor window changes
addEventListener("resize", setRem)

12. Handwritten publish and subscribe

Both publish and subscribe in publish subscribe mode are handled by a scheduling center

The publish subscribe mode is completely decoupled, because the logic processing function is directly stored in the scheduling center

Key points: Add / delete / distribute and update three events.

class Event {
  // First, define an event container to hold the event array (because subscribers can be multiple)
  #handlers = {}

  // Event addition method. The parameters include event name and event method
  addEventListener(type, handler) {
    // First, judge whether there is a type event container in handlers. If not, create a new array container
    if (!(type in this.#handlers)) {
      this.#handlers[type] = []
    }
    // Save event to
    this.#handlers[type].push(handler)
  }

  // Two parameters of trigger event (event name, parameter)
  dispatchEvent(type, ...params) {
    // If the event is not registered, an error is thrown
    if (!(type in this.#handlers)) {
      return new Error('The event is not registered')
    }
    // Convenience trigger
    this.#handlers[type].forEach(handler => {
      handler(...params)
    })
  }

  // Event removal parameters (event name, deleted event, if there is no second parameter, the subscription and publication of the event will be deleted)
  removeEventListener(type, handler) {
    // Invalid event thrown
    if (!(type in this.#handlers)) {
      return new Error('Invalid event')
    }
    if (!handler) {
      // Remove events directly
      delete this.#handlers[type]
    } else {
      const idx = this.#handlers[type].findIndex(ele => ele === handler)
      // Throw exception event
      if (idx === -1) {
        return new Error('No such binding event')
      }
      // Remove event
      this.#handlers[type].splice(idx, 1)
      if (this.#handlers[type].length === 0) {
        delete this.#handlers[type]
      }
    }
  }
}

13. Implementation of array de duplication

This question is more flexible. You can choose the most convenient writing method according to the question.

Implementation of JS array de duplication

14. Array flattening

See below:

JS array flattening

15. Realize Fibonacci sequence

reference resources: Several ways of realizing Fibonacci sequence with js

16. Lazy loading of pictures

Different from ordinary image lazy loading, the following two more elaborate processes are made:

  • Remove event listening after all pictures are loaded;
  • The loaded image is removed from imgList;
let imgList = [...document.querySelectorAll('img')]
let length = imgList.length

const imgLazyLoad = function() {
    let count = 0
    return (function() {
        let deleteIndexList = []
        imgList.forEach((img, index) => {
            let rect = img.getBoundingClientRect()
            if (rect.top < window.innerHeight) {
                img.src = img.dataset.src
                deleteIndexList.push(index)
                count++
                if (count === length) {
                    document.removeEventListener('scroll', imgLazyLoad)
                }
            }
        })
        imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
    })()
}

// It's best to add anti shake treatment here
document.addEventListener('scroll', imgLazyLoad)

17. Implement a JSONP

Core principle of JSONP: the script tag is not constrained by the homology policy, so it can be used for cross domain requests. The advantage is good compatibility, but it can only be used for GET requests.

const jsonp = ({ url, params, callbackName }) => {
    const generateUrl = () => {
        let dataSrc = ''
        for (let key in params) {
            if (params.hasOwnProperty(key)) {
                dataSrc += `${key}=${params[key]}&`
            }
        }
        dataSrc += `callback=${callbackName}`
        return `${url}?${dataSrc}`
    }
    return new Promise((resolve, reject) => {
        const scriptEle = document.createElement('script')
        scriptEle.src = generateUrl()
        document.body.appendChild(scriptEle)
        window[callbackName] = data => {
            resolve(data)
            document.removeChild(scriptEle)
        }
    })
}

Recommended articles:

Big front end basic knowledge points and interview hardest hit areas learning directory_ Paper airplane blog - CSDN blog

2021 front end interview js topic summary, you might as well see if there is a question that belongs to you_ Paper airplane blog - CSDN blog

Let's talk about macro tasks and micro tasks in js_ Paper airplane blog - CSDN blog

Tags: Javascript Front-end

Posted on Sun, 19 Sep 2021 03:04:25 -0400 by dvt85