JavaScript Handwritten Code Interview Questions

1. Write the code part

1. Implement a new operator

        function myNew(func) {
            //1.创建一个空的对象
            var obj = {};
            //2.将新创建的新对象的隐形原型指向构造函数的显示原型
            obj.__proto__ = func.prototype;
            //由于arguments是类数组,需要转换才能调用数组的方法,需要从第二个开始获取参数
            var agrs=[...arguments].slice(1)
            //改变this指向并获取执行结果
            var res = func.apply(obj, agrs)
            return typeof res === 'object' ? res : obj
        }

        function A(name, age) {
            this.name = name;
            this.age = age;
        }

        var obj1 = myNew(A, 'hzz', 18);
        //等价于
        var obj2=new A('hzz',18);
        console.log(obj1)

2. Implement a call or apply function:

2.1 call syntax

         func.call(thisArg,arg1,arg2,...) , call a function with a specified this pointer and list parameters

2.2 call implementation

The basic idea:

  • 1. Determine whether the input is a function object, if not, an error message will be prompted
  • 2. Determine whether the incoming context exists, if not, point to window
  • 3. Process the incoming parameters, that is, intercept all parameters except the first parameter
  • 4. Bind the method to the property of the context object (this is the key to become the specified object call)
  • 5. Get the result after the call, and delete the newly added attribute (context.fn)
       //下面是call函数的实现
        //注意:不管是call还是apply都必须是函数调用,只是传入call或者apply参数的可以是任意对象
        //没有Array.call
        Function.prototype.myCall = function (context) {
            var _this = this;
            var res = null; //用于存储结果
            //1.判断是否为函数调用
            if (typeof _this !== 'function') {
                throw new TypeError('出错了')
            }
            //2.判断传入的对象是否存在
            context = context || window
            context.fn = this;
            //3.获取参数
            var agrs = [...arguments].slice(1)
            //调用的函数添加为传入对象的一个属性
            res = context.fn(...args)
            //删除新增的属性
            delete context.fn;
            //返回结果
            return res
        }


        //*******下面是测试代码***********
        var obj1 = {
            name: 'hzz',
            age: 18,
            sayName: function (xx) {
                console.log(this.name)
            }
        }
        var obj2 = {
            name: 'xxx'
        }
        obj1.sayName.myCall(obj2, 'ss')//xxx,这里的'ss'是无用参数


        var arr1 = [1, 2, 3, 4]
        var arr2 = [4, 8, 9, 12]
        const arr3 = arr1.filter.myCall(arr2, function (item) {
            return item > 2
        })
        console.log(arr3)// [4, 8, 9, 12]

2.4 apply syntax

       func.call(thisArg,[argsArray]) , calls a function with a specified this pointer and array parameters (or array-like objects)

2.5apply implementation

Basic idea: the same as call, but there are differences in the processing of parameters

        Function.prototype.myApply = function (context) {
            var res = null;
            if (typeof this !== 'function') {
                throw new TypeError('error')
            }
            context = context || window;
            context.fn = this;
            var args = [...arguments].slice(1);
            //处理参数不一样,即判断当有参数的时候传入参数调用,没有参数的时候直接调用
            if (arguments[1]) {
                res = context.fn(...arguments[1])
            } else {
                res = context.fn()
            }
            delete context.fn
            return res
        }

        //********测试代码************
        const arr4 = arr2.filter.myApply(arr1, [function (item) {
            return item > 2
        }])
        console.log(arr4)

2.6 Upgrade the simple version of call and apply

        Function.prototype.myCall = function (context, ...args) {
            context = context || window;
            //创造唯一值,作为我们构造的context内部的方法名
            let fn = Symbol();
            //this指向调用call的函数
            context[fn] = this;
            //执行函数 并返回结果(相当于把自身作为传入context的方法进行调用了
            return context[fn](...args)
        }

        Function.prototype.myApply = function (context, agrs) {
            context = context || window;
            var fn = Symbol();
            context[fn] = this;
            return context[fn](...agrs)
        }

3. Implementation of bind function (to be)

4. Implement instanceof

        function myInstanceof(left, right) {
            while (true) {
                if (left === null) {
                    return false
                }
                if (left.__proto__ === right.prototype) {
                    return true
                }
                //循环的条件
                left = left.__proto__
            }
        }

5. Array deduplication

//方法1 使用set
function uniqueArr(arrr{
    return [...new Set(arr)];
}

//方法二 使用filter
function uniqueArr(arr){
    return arr.filter((item,index)=>{
        return arr.indexOf(item)===index;
    }
}

6. Array flattening

//直接使用flat函数,其中默认降2维,可以传入无限大数据使得不管多少维数组全部降为一维数组
const res1=arr.flat(Infinity)

7. Implement a js inheritance method that you think is good

What is implemented here is the inheritance of parasite combination, the idea is the inheritance before the two constructors

The implementation code is as follows:

    function Person(name, age) {
      this.name = name;
      this.age = age;
      this.sayName = function () {
        console.log(this.name)
      }
    }
    Person.prototype.running = function () {
      console.log('runnig')
    }

    Person.prototype.eating = function () {
      console.log('eating')
    }


    function Student(name, age, sno, friends) {
      Person.call(this, name, age);
      this.sno = sno;
      this.friends = friends;
    }

    Student.prototype = Object.create(Person.prototype)
    Student.prototype.constructor = Student//细分构造函数的类型
    console.log(Student.prototype)
    let obj = new Student('hzz', 18, '171010204', 'lhk')
    console.log(obj)
    obj.running()

8. Anti-shake and throttling

Since I have seen the principle of anti-shake and throttling a long time ago, it is because I just came into contact with the front end when I saw it at that time, so I still have a little understanding, and I can’t tell the similarities and differences between t anti-shake and throttling. I will reorganize here today. Let’s take a look at the principles and make a distinction

8.1 Anti-shake

1. understand

        Anti-shake means that when you trigger an event, the corresponding function will not be triggered immediately, but will be triggered after a certain period of time; for example, if there is an input box and I want to enter "abc d" to search, if If you don’t do anti-shake processing, then every time you input a, b, ... will trigger an event, when you have done anti-shake processing, ① Then after you input abc, you have not done it again after a certain period of time Enter, then the event will be triggered at this time, and suppose you stay for a certain period of time after entering abc, and when you enter d again, the time you stay at that time < the time you set, then this time will be when you enter again It will recalculate, and then repeat ① if it goes on like this, it is anti-shake;

2. The benefits of anti-shake

        Reduce the number of requests sent to the server, tigao performance

3. The application of anti-shake

  • input box input
  • Click the button that submits the event
  • screen scrolling

4. Realization of the principle of anti-shake

    function debounce(fn, waitTime) {
      let timer = null;
      //返回这个函数,倒是触发事件的时候才触发这个函数
      return function (...args) {
        if (timer) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          //由于这个fn是传递进来的函数,且是独立调用的,因此
          //因此this指向并不是指向我触发的对象
          //且我们触发对象触发的函数实质是返回的函数,因此this指向我们可以从返回中的函数中获取
          //这里是箭头函数,所以可以直接向上查找
          fn.apply(this, args)
        }, waitTime)
      }
    }
   //下面是测试
    let count = 0
    const inputChange = function (event) {
      console.log(`触发了${++count}事件`, this, event)
    }
    //获取input原始
    let item = document.querySelector("input")
    //这里是没有做防抖的是,触发事件回立即执行inputChange函数
    // item.oninput = inputChange
    //这里是做了防抖处理
    item.oninput = debounce(inputChange, 1000)

5. Upload screenshots for easy understanding when looking back in the future

 8.2 Throttling

        The throttling function is to control the frequency of time triggers within a certain period of time, no matter how many times it is triggered, this frequency is fixed

application:

    function thortter(fn, await) {
      //leading表示最后
      //记录上一次的开始时间,将lastTime放在外面的原因是,这个lastTime是变化的,我们需要记录其值
      //如果放在函数内部的话,那么每次触发事件函数的时候,这个lastTime都是新的
      let lastTime = 0;
      const _throtter = function (...args) {
        //每次触发函数的时候都可以得到最新的时间
        const nowTime = new Date().getTime()
        //当lastTime时间与当前时间差大于或者等于设置的频率时间的时候,就触发函数
        if (nowTime - lastTime >= await) {
          fn.apply(this, args)
          //更新改变
          lastTime = nowTime;
        }
      }
      return _throtter;
    }

9. Realize deep copy

        The copy function we mentioned before can only copy the first layer. When the copied data is a reference type, only the reference of the object is copied, such as copying between a={} and b={}, then because a , b is the data of the reference type, then the data changes between the two affect each other, because only the reference address of one of the objects is copied, so the deep copy is implemented next, so that the copied data does not affect each other

①In the primary version, it does not take into account the assumption that the object attribute points to itself when copying

        Next, customize a deep copy function. The main functions are as follows:

  • If the incoming type is Symbol, directly return the newly created New Symbol(originalValue.description)
  • If the input is a function type, return it directly (if you want to copy a new function, it is very complicated, and the function of the function is to achieve reusability, so here we can directly return the function)
  • If the data of type Set or Map is passed in, the new creation returns: new Set([...originalValue]), new Map([...originalValue]
  • If the value passed in is not the object type we use isObject(obj) to judge, it means that it is the original value, and return directly
  • The next step is to traverse and recursively clone when the incoming value is an object, but note that when the attribute is an array, special handling is required: const newObject = Array.isArray(originalValue) ? [] : {}
  • Enter the last traversal recursive call to indicate that the attribute at this time belongs to the object, use newObject[key] = deepClone(originalValue[key])
  • Finally return the newly created object newObject
//判断是否为对象
    function isObject(obj) {
      return typeof obj !== "null" && (typeof obj === "object" || typeof obj === "function")
    }

    function deepClone(originalValue) {
      //0.由于默认情况下是不对Symbol数据类型进行拷贝的
      //如果是则新穿件一个Symbol
      if (typeof originalValue === "symbol") {
        return Symbol(originalValue.description)
      }

      //1.当不是对象数据类型的时候,直接返回
      if (!isObject(originalValue)) {
        return originalValue
      }
      //2.对属性为函数的时候进行处理
      if (typeof originalValue === 'function') {
        return originalValue
      }
      //判断是否为set数据类型
      if (originalValue instanceof Set) {
        return new Set([...originalValue])
      }
      //判断是否为map类型
      if (originalValue instanceof Map) {
        return new Map([...originalValue])
      }
      //3.这里主要是对数组的处理,当被克隆的对象的属性是数组的时候,返回的应当是数组类型
      const newObject = Array.isArray(originalValue) ? [] : {}
      for (const key in originalValue) {
        //进入这里说明传入的值是一个对象的数据,因此这里遍历这个对象
        newObject[key] = deepClone(originalValue[key])
      }
      return newObject
    }

    //测试代码
    let s1 = Symbol('a')
    const obj = {
      name: 'hzz',
      age: 18,
      sym: s1,
      arr1: [1, 2, 2, 3],
      friends: {
        name: 'wzj',
        age: 22
      },
      sayName: function () {
        console.log(this.name)
      },
      set1: new Set(['aaa', 'bbb', 'ccc']),
      map1: new Map([["aaa", "bbb"], ["vvv", "bbbb"]])
    }

    var newObj = deepClone(obj)
    obj.friends.name = "xxxx"
    console.log(newObj.friends.name)
    console.log(newObj)
    newObj.sayName()

②Upgrade the version and consider the situation where the object attribute points to itself

 //判断是否为对象
    function isObject(obj) {
      return typeof obj !== "null" && (typeof obj === "object" || typeof obj === "function")
    }

    function deepClone(originalValue, map = new Map()) {
      //0.由于默认情况下是不对Symbol数据类型进行拷贝的
      //如果是则新穿件一个Symbol
      if (typeof originalValue === "symbol") {
        return Symbol(originalValue.description)
      }

      //1.当不是对象数据类型的时候,直接返回
      if (!isObject(originalValue)) {
        return originalValue
      }
      //2.对属性为函数的时候进行处理
      if (typeof originalValue === 'function') {
        return originalValue
      }
      //判断是否为set数据类型
      if (originalValue instanceof Set) {
        return new Set([...originalValue])
      }
      //判断是否为map类型
      if (originalValue instanceof Map) {
        return new Map([...originalValue])
      }

      //表示当有的时候直接返回,防止无线循环
      if (map.has(originalValue)) {
        return map.get(originalValue)
      }

      //3.这里主要是对数组的处理,当被克隆的对象的属性是数组的时候,返回的应当是数组类型
      const newObject = Array.isArray(originalValue) ? [] : {}
      //设置指向自己的循环
      map.set(originalValue, newObject)
      for (const key in originalValue) {
        //进入这里说明传入的值是一个对象的数据,因此这里遍历这个对象
        newObject[key] = deepClone(originalValue[key], map)
      }
      return newObject
    }


    //测试代码中加入
    obj.info = obj

10. What is promise? Can I write one by hand?

  Promises are a solution to asynchronous programming

A Promise has the following states:

  • pending: initial waiting state, neither successful nor failed
  • fulfilled: think this operation is completed
  • reject: think this operation failed

And it should be noted that once the waiting state of Promise is changed, it will not change again, that is to say, once it becomes fulfilled or rejected, it cannot be changed again. Once the state of pending changes, the then method of the Promise object will be called, otherwise the catch will be triggered

 new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(1);
        resolve()
      }, 100)
    }).then(res => {
      setTimeout(() => {
        console.log(2);
      }, 2000)
    }).then(res => {
      setTimeout(() => {
        console.log(3)
      }, 3000)
    }).catch((err) => {
      console.log(err)
    })

The following is the handwritten implementation of Promise, enough for interviews!

 //Promise的手写实现
    function muPromise(constructor) {
      let _this = this;
      //1.定义状态改变前的初始状态
      _this.status = 'pending';
      //2.分别订货状态为resolved和rejected的时候的状态
      _this.value = undefined;
      _this.reason = undefined;
      function resolve(value) {
        //使用“===”,保证了状态改变是不可逆的
        //这里表示不管你执行了resolve还是reject回调函数的时候,其其实的状态只能是‘pending’
        //所以需要提前判断,所以进入回调函数之后,改变其状态
        if (_this.status === 'pending') {
          _this.value = value;
          _this.status = 'resolved';
        }
      }
      function reject(reason) {
        if (_this.status === 'pending') {
          _this.reason = reason;
          _this.status = "rejected"
        }
      }
      //捕获构造函数异常
      try {
        //将resolve,reject作为回调函数的参数,这个回调函数又是Promise构造函数的参数
        //类似new Promise((resolve,reject)=>{})
        constructor(resolve, reject);
      } catch (e) {
        console.log(e)
      }
    }


    //定义链式调用then方法
    myPromise.prototype.then = function (onFullfilled, onRejected) {
      //传入的onFulfilled、onRejected均是回调函数
      let _this = this;
      switch (_this.status) {
        case 'resolved':
          //即将resolve(value)中的参数回调到onFullfilled回调函数中
          onFullfilled(_this.value);
          break;
        case "rejected":
          //即将resolve(value)中的参数回调到onRejected回调函数中
          onRejected(_this.reason);
          break;
        default:
      }
    }

11. Handwritten Promise.all

        Generally speaking, Promise.all is used to handle multiple concurrent requests, and the data of different interfaces used in a page are requested together. However, in Promise.all, if one of the interfaces fails, then multiple requests will also be processed. If it fails, the page may not come, it depends on the coupling degree of the current page.

Implementation idea:

  • Accepts an array of Promise instances or an object with the Iterator interface as a parameter
function promisAll(promises) {
      return new Promise((resolve, reject) => {
        if (!Array.isArray(promises)) {
          throw new TypeError(`argument must be a array`)
        }
        var resolvedCounter = 0;
        var promisesNum = promises.length
        var resolveResult = []//存储所有返回值结果
        for (let i = 0; i < promisesNum; i++) {
          Promise.resolve(promises[i]).then(value => {
            resolvedCounter++;
            resolveResult[i] = value
            if (resolvedCounter === promises.length) {
              return resolve(resolveResult)
            }
          }, error => {
            return reject(error)
          })
        }
      })
    }

    let p1 = new Promise((resolve, reject) => {
      setTimeout(function () {
        resolve(1)
      }, 1000)
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(function () {
        resolve(2)
      }, 2000)
    })
    let p3 = new Promise((resolve, reject) => {
      setTimeout(function () {
        resolve(3)
      }, 3000)
    })

    promisAll([p1, p2, p3]).then(res => {
      console.log(res)
    })

12. Implement Promise.race

13. Implement the bind function

14. Implement Ajax requests

        Can refer to: Five steps of Ajax request_weixin_45846357's blog-CSDN blog_Five steps of ajax request

        Ajax is a way of asynchronous requests, and ajax asynchronous requests will not refresh the entire page, but also a partial update

Steps for ship ajax request:

  1. Create a new XMLHttpRequest asynchronous object
var xhr = new XMLHttpRequest();

    2. Use the open method on this object to create an HTTP request. The parameters required by the open method are the request method, the address of the request, whether it is asynchronous and the user's authentication information

// get请求如果有参数就需要在url后面拼接参数,
// post如果有参数,就在请求体中传递 xhr.open("get","validate.php?username="+name)
xhr.open("post","validate.php");

    3. Set the request body send()

// 1.get的参数在url拼接了,所以不需要在这个函数中设置
// 2.post的参数在这个函数中设置(如果有参数)
xhr.send(null) xhr.send("username="+name);

      4. Let the asynchronous object receive the response data from the server. There are two conditions for a successful response:

  • The server successfully responded
  • The response status of the asynchronous object is 4 (the data can be used after parsing)
xhr.onreadystatechange = function(){ 
if(xhr.status == 200 && xhr.readyState == 4){ 
 console.log(xhr.responseText);
 }

The specific implementation is as follows:

    const baseURL = '/server';
    let xhr = new XHRHttpRequest();
    xhr.open('get', baseURL, true);
    xhr.send(null)
    xhr.onreadystatechange = function () {
      if (this.status === 200 && this.readystate === 4) {
        console.log(this.response)
      } else {
        console.log(this.statusText)
      }
    }

 15. Use promise to encapsulate ajax

    //使用promise封装ajax请求
    function getJson(url) {
      return new Promise((resolve, reject) => {
        let xhr = new XHRHttpRequest();
        xhr.open('get', url, true);
        xhr.send(null);
        xhr.onreadystatechange = function () {
          if (this.status === 200 && this.readystate === 4) {
            resolve(this.response);
          } else {
            reject(this.statusText)
          }
        }
      })
    }

16. Implement shallow copy

        Shallow copy refers to a new object that accurately copies the attribute values ​​of the original object. If the copy is a basic data type, the copy is the value of the basic data type. If it is a reference type, then the copy is the reference address of the reference type. , if the reference address of one of the objects changes, the other object will also change

The ways to implement shallow copy are as follows:

1.Object.assign()

2. Spread operator: {...obj}

3. The array method realizes the shallow copy of the array. slice(), this method can return the selected element from the array, usage: array.slice(start, end), this method will not change the original array, two parameters are optional, When not writing, you can copy an array var copyArr=[1,2,3].slice()

17. Handwritten Object.create

Let's take a look at its role first:

    const f = myCreate(obj)
    console.log(f.name)
    const person = {
      isHuman: false,
      printIntroduction: function () {
        console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
      }
    };
    const me = Object.create(person); // me.__proto__ === person
    me.name = "Matthew"; // name属性被设置在新对象me上,而不是现有对象person上
    me.isHuman = true; // 继承的属性可以被重写
    me.printIntroduction(); // My name is Matthew. Am I human? true

Idea: use the incoming object as a prototype

    function myCreate(obj) {
      function F() { }
      F.prototype = obj;
      return new F()
    }

2. Scenario reflection part

1. Use promise to realize asynchronous loading of pictures

    let imageAsync = (url) => {
      return new Promise((resolve, reject) => {
        let img = new Image();
        img.src = url;
        img.onload = () => {
          console.log('图片请求成功,此处理进行通用操作')
          resolve(image)
        }
        img.onerror = (err) => {
          console.log('失败,处处进行失败的通用操作')
          reject(err)
        }
      })
    }

    imageAsync("url").then(() => {
      console.log("加载成功")
    }, () => {
      console.log("加载失败")
    })

2. Realize two-way data binding

    let obj = {}
    let input = document.querySelector('input')
    let span = document.querySelector("span")
    //数据劫持
    Object.defineProperty(obj, "text", {
      configurable: true,
      enumerable: true,
      get() {
        console.log('获取数据了')
      },
      set(newVal) {
        console.log('数据更新了')
        input.value = newVal;
        span.innerHTML = newVal;
      }
    })
    //输入监听
    input.addEventListener('keyup', function (e) {
      obj.text = e.target.value
    })

3. Use setTimeout to implement setInterval

        The function of setInterval is to execute a function every once in a while, but this execution is not really executed immediately when the time comes, its real function is to add time to the time queue every other time, only when the current execution stack is empty , to take out the time from the time queue for execution. All possible situations may occur, that is, when the executive executes for a long time, the event queue will accumulate multiple timer-added events. When the execution stack ends, These events will be executed sequentially, so they cannot have the effect of executing at intervals

        For this shortcoming of setInterval, we can use setTimeout recursive calls to simulate setInterval, so that we can ensure that only one event ends, and we will trigger the next timer, which solves the problem of setInterval

Use recursive functions when implementing ideas, and continuously execute setTimeout to achieve the effect of setInterval

function mySetInterval(fn, timeout) {
  // 控制器,控制定时器是否继续执行
  var timer = {
    flag: true
  };
  // 设置递归函数,模拟定时器执行。
  function interval() {
    if (timer.flag) {
      fn();
      setTimeout(interval, timeout);
    }
  }
  // 启动定时器
  setTimeout(interval, timeout);
  // 返回控制器
  return timer;
}

4. Implement jsonp

    function addScript(scr, callBack) {
      const script = document.createElement('script');
      script.src = scr;
      script.type = "text/javascript";
      document.appendChild(script)
    }
    function handleRes(res) {
      console.log(res)
    }
    addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");
    // 接口返回的数据格式
    handleRes({ a: 1, b: 2 });

Guess you like

Origin blog.csdn.net/weixin_46872121/article/details/123254290