preface

Interview review summary: Refer to Yuhu Yu’s blog

1. Implement a new operator

  • First the function takes unquantified arguments. The first argument is the constructor, and the following arguments are used by the constructor
  • Then internally create an empty object obj
  • Because obj objects need to access properties on the constructor prototype chain, we link the two with obj.proto = func.prototype.
  • Call or apply to point the constructor’s this to obj
  • Check whether the constructor returns an object. If it is, use the value returned by the constructor, otherwise use obj, thus ignoring the original value returned by the constructor
function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.add = function() {
    console.log(this.name)
    console.log(this.age)
}
function objectFactory(func) {
    var obj = {}
    var arg = [...arguments].slice(1)
    obj.__proto__ = func.prototype;
    var res = func.apply(obj, arg)
    if ((typeof res === 'Object' || typeof res === 'Function') && res ! == null) {return res 
    }
    return obj
}
var person = objectFactory(Person, 'zh'18),Copy the code

2. Implement a Call apply or bind

2.1 the call

fun.call(thisArg, arg1, arg2, ...)

let obj = {
    value: 1
}
function bar(name, age) {
    console.log(this.value)
    console.log(name)
    console.log(age)
}
bar.call(obj, 'zh'18),Copy the code

Note:

  1. Call changes the function this to point to bar
  2. All arguments except the first are passed in one by one
  3. Bar function execution

You can simulate steps 1,2 below

  1. Sets the function as a property of the object
  2. Execute the function
  3. Delete the function
obj.fn = bar
obj.fn()
delete obj.fn()
Copy the code

The full version

Function.prototype.myCall = function(context) {
    context = context || window
    let args = [...arguments].slice(1)
    context.fn = this
    letres = context.fn(... args) delete context.fnreturn res
}
bar.myCall(obj, 'zh'18),Copy the code

2.2 the apply

Function.prototype.myApply = function(context) {
    let res
    context = context || window
    context.fn = this
    if (arguments[1]) {
        context.fn(args)
    } else {
        context.fn()
    }
    delete context.fn
    return res
}
bar.myApply(obj, ['zh'18]),Copy the code

2.3 the bind

The bind() method creates a new function. When the new function is called, the first argument of bind() will be this when it runs, and the remaining arguments will be arguments to the new function. Using bind is the tricky part of the call, considering that the returned function can also be instantiated as a constructor

Function.prototype.myBind = function(context) {
    if(typeof this ! ="function") {
        throw Error("not a function")}let fn = this
    let args = [...arguments].slice(1)
    let res =  function() {// If it is a constructor, this (barbind instance) points to res (new barbind constructor)true// If this is a normal function, this points to windowfalseTo bind the function's this to contextreturnfn.apply(this instanceof res ? this : context,args.concat(... Prototype = this.prototype (); // res.prototype = this.prototype ()function temp() {}
    temp.prototype = this.prototype 
    res.prototype = new temp()
    return res
}
var obj = {
    value: 1
}
function Bar(name,age) {
   console.log(this.value)
   console.log(name, age)
   this.name = name
   this.age = age
}
Bar.prototype.add = function() {
    console.log(this.name)
}
var barBind = Bar.myBind(obj)
var barbind = new barBind('zh'18),Copy the code

3. Debouncing and Throttling

3.1 if you

Event continues to fire, wait n seconds after the event stops firing to execute the function, if the event is triggered within n seconds, then I will use the time of the new event to prevail, n seconds after the execution.

function debounce(func, delay) {
    let timeout;
    return function () {
        const context = this;
        const args = arguments;
        clearTimeout(timeout)
        timeout = setTimeout(() => { func.apply(context, args) }, delay); }} ** New requirements: 1, event trigger, immediately execute function, then wait n seconds after the trigger stopped to execute function. Wait n seconds after the event stops being triggered before running the function. 3. Add the immediate parameter to check whether the function is executed immediatelyfunction debounce(fn, delay, immediate=false) {let timeout = null;
    function debounce(fn, delay, immediate = false) {
      let timeout = null;
      return function (args) {
        let context = this;
        const args = arguments;
        if (timeout) clearTimeout(timeout)
        if(immediate) {// When triggered for the first time, timeout is null and a callback is executed immediately. immediate =false
        } else {
          timeout = setTimeout(function() { fn.apply(context, args); }, delay)}}}} ** New requirements: 1, event trigger, immediately execute function. Wait n seconds before the event is triggered again. 2. Add the immediate parameter to check whether the event is executed immediatelyfunction debounce(func, wait, immediate=flase) {
    var timeout;
    return function () {
        var context = this;
        var args = arguments;
        if (timeout) clearTimeout(timeout); 
        if (immediate) {
            if(! Func. Apply (context, args) //**clearTimeout(timeout) clears timer tasks while timeout is still in timeout =setTimeout(function(){
                timeout = null;
            }, wait)}else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait); }}} ** New requirements: 1, event trigger, immediately execute function, then wait n seconds after the trigger stopped to execute function. 2. The event fires again, executes the function immediately, and waits n seconds after the event stops firing before executing the function. 3. Add the immediate parameter to check whether the command is executed immediatelyfunction debounce(func, delay, immediate) {
      var timeout
      return function() {
        var context = this;
        var args = arguments;
        if (timeout) clearTimeout(timeout)
        if (immediate) {
          if(! timeout) func.apply(context, args) timeout =setTimeout(function () {
            func.apply(context, args)
            timeout = null
          }, delay)
        } else {
          timeout = setTimeout(function() {func.apply(context, args)}, delay)}}}} ** Final versionfunction debounce(func, wait, immediate) {
    var timeout, result;
    var debounced = function () {
        var context = this;
        var args = arguments;
        if (timeout) clearTimeout(timeout);
        if(immediate) {// When immediate=trueReturns the result of the function execution // if immediate=false setThe return value of Timeout is an ID value and cannot return the result of the function executionif (!timeout) result = func.apply(context, args) 。
            timeout = setTimeout(function () {
                func.apply()
                timeout = null
            }, delay)
        }else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
        return result;
    };
    debounced.cancel = function() {// Cancel the debounce function clearTimeout(timeout); timeout = null; };return debounced;
}
Copy the code

Application scenarios of the function shock prevention Continuous events that only need to trigger a callback are as follows:

  • The search box searches for input. The user only needs to type one last time before sending the request
  • Window size Resize. Just after the window adjustment is complete, calculate the window size. Prevent repeated rendering.

3.2 the throttle

Throttling is a function that executes every n seconds when continuously triggered. There are two main implementations: one is to use a timestamp, and the other is to set a timer.

* * timestampfunction throttle(func, wait) {
    var context, args;
    var previous = 0;

    return function() {
        var now = +new Date();
        context = this;
        args = arguments;
        if (now - previous > waitApply (context, args); previous = now; }}} ** timerfunction throttle(func, wait) {
    var timeout;

    return function() {
        context = this;
        args = arguments;
        if(! timeout) { timeout =setTimeout(function(){timeout = null for the first time after n seconds; func.apply(context, args) },wait)}}} the difference: - the first events are executed immediately, the second event will be the first execution in n seconds - the first event there is no way to stop the trigger execution events, after the second stop trigger events will continue to perform again event * * new demand: event trigger function immediately, stop trigger also performs a functionfunction throttle(func, wait=1000) {
    var timeout, context, args, result;
    var previous = 0;
    var throttled = function() {
        context = this;
        args = arguments;
        var now = +new Date();
        var remaining = wait- (now - previous); // The first time: now-previouse<=1000 remaining<0 // The second time: now-previouse<=1000 remaining=1000&&! After timeout 1000 seconds execute the function, and so on // leave: 1, Now-previouse <1000 0<remaining<=1000 1000 seconds after the function is executed, // 2, now-previouse>1000 remaining<0 immediatelyif (remaining <= 0 || remaining > wait) {// If there is no time left or you change the system timeif (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            func.apply(context, args);
            previous = now;
        } else if(! timeout) { timeout =setTimeout(function() { previous = +new Date(); func.apply(context, args) timeout = null; }, remaining); }};returnthrottled; } ** select * from (select * from (select * from (select * from (select * from)));false3. Trailing:falseDisables the stop-triggered callbackfunction throttle(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if(! options) options = {}; var throttled =function() {
        context = this;
        args = arguments;
        var now = +new Date();
        if(! previous && options.leading ===false) previous = now;
        var remaining = wait - (now - previous);
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            func.apply(context, args);
            previous = now;
        } else if(! timeout && options.trailing ! = =false) {
            timeout = setTimeout(function() { func.apply(context, args); previous = +new Date(); timeout = null; }, remaining); }}; throttled.cancel =function() {
        clearTimeout(timeout);
        previous = 0;
        timeout = null;
    }
    return throttled;
}
Copy the code

The application scenarios of function throttling are as follows:

  • Scroll to load, load more or roll bottom to listen
  • Google search box, search association function
  • Click submit frequently, and the form is submitted repeatedly