ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022必会的前端手写面试题

2022-05-01 14:04:07  阅读:171  来源: 互联网

标签:function 面试题 return 数组 getName 2022 new 手写 Foo


面试题视频讲解(高效学习):进入学习

二、题目

1. 防抖节流

这也是一个经典题目了,首先要知道什么是防抖,什么是节流。

  • 防抖: 在一段时间内,事件只会最后触发一次。

  • 节流: 事件,按照一段时间的间隔来进行触发。

    实在不懂的话,可以去这个大佬的Demo地址玩玩防抖节流DEMO

     // 防抖
    function debounce(fn) {
      let timeout = null; 
      return function () {
        // 如果事件再次触发就清除定时器,重新计时
        clearTimeout(timeout);
        timeout = setTimeout(() => {
          fn.apply(this, arguments);
        }, 500);
      };
    }
    
    // 节流
    function throttle(fn) {
      let flag = null; // 通过闭包保存一个标记
      return function () {
        if (flag) return; // 当定时器没有执行的时候标记永远是null
        flag = setTimeout(() => {
          fn.apply(this, arguments);
           // 最后在setTimeout执行完毕后再把标记设置为null(关键)
           // 表示可以执行下一次循环了。
          flag = null;
        }, 500);
      };
    }
    
复制代码

这道题主要还是考查对 防抖 节流 的理解吧,千万别记反了!

2.一个正则题

要求写出 区号+8位数字,或者区号+特殊号码: 10010/110,中间用短横线隔开的正则验证。 区号就是三位数字开头。

例如 010-12345678

 let reg = /^\d{3}-(\d{8}|10010|110)/g
复制代码

这个比较简单,熟悉正则的基本用法就可以做出来了。

3. 不使用a标签,如何实现a标签的功能

 // 通过 window.open 和 location.href 方法其实就可以实现。 
 // 分别对应了a标签的 blank 和 self 属性
复制代码

4. 不使用循环API 来删除数组中指定位置的元素(如:删除第三位) 写越多越好

这个题的意思就是,不能循环的API(如 for filter之类的)。


var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 方法一 : splice 操作数组 会改变原数组 
arr.splice(2, 1)


// 方法二 : slice 截取选中元素 返回新数组 不改变原数组
arr.slice(0, 2).concat(arr.slice(3,))

// 方法三 delete数组中的元素 再把这个元素给剔除掉
delete arr[2]
arr.join("").replace("empty", "").split("")
复制代码

5. 深拷贝

深拷贝和浅拷贝的区别就在于

  • 浅拷贝: 对于复杂数据类型,浅拷贝只是把引用地址赋值给了新的对象,改变这个新对象的值,原对象的值也会一起改变
  • 深拷贝: 对于复杂数据类型,拷贝后地址引用都是新的,改变拷贝后新对象的值,不会影响原对象的值

所以关键点就在于对复杂数据类型的处理,这里我写了两种写法,第二中比第一种有部分性能提升

const isObj = (val) => typeof val === "object" && val !== null;

// 写法1
function deepClone(obj) {
    // 通过 instanceof 去判断你要拷贝的变量它是否是数组(如果不是数组则对象)。

    // 1. 准备你想返回的变量(新地址)。
    const newObj = obj instanceof Array ? [] : {}; // 核心代码。

    // 2. 做拷贝;简单数据类型只需要赋值,如果遇到复杂数据类型就再次进入进行深拷贝,直到所找到的数据为简单数据类型为止。
    for (const key in obj) {
        const item = obj[key];
        newObj[key] = isObj(item) ? deepClone(item) : item;
    }

    // 3. 返回拷贝的变量。
    return newObj;
}




// 写法2 利用es6新特性 WeakMap弱引用 性能更好 并且支持 Symbol
function deepClone2(obj, wMap = new WeakMap()) {
  if (isObj(obj)) {
    // 判断是对象还是数组
    let target = Array.isArray(obj) ? [] : {};

    // 如果存在这个就直接返回
    if (wMap.has(obj)) {
      return wMap.get(obj);
    }

    wMap.set(obj, target);

    // 遍历对象
    Reflect.ownKeys(obj).forEach((item) => {
      // 拿到数据后判断是复杂数据还是简单数据 如果是复杂数据类型就继续递归调用
      target[item] = isObj(obj[item]) ? deepClone2(obj[item], wMap) : obj[item];
    });

    return target;
  } else {
    return obj;
  }
}
复制代码

这道题主要是的方案就是,递归加数据类型的判断

如是复杂数据类型,就递归的再次调用你这个拷贝方法 直到是简单数据类型后可以进行直接赋值

6. 手写call bind apply

call bind apply的作用都是可以进行修改this指向

  • call 和 apply的区别在于参数传递的不同
  • bind 区别在于最后会返回一个函数。

    // call
    Function.prototype.MyCall = function (context) {
      if (typeof this !== "function") {
        throw new Error('type error')
      }
      if (context === null || context === undefined) {
        // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window
      } else {
        // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
        context = Object(context)
      }

      // 使用Symbol 来确定唯一
      const fnSym = Symbol()

      //模拟对象的this指向
      context[fnSym] = this

      // 获取参数
      const args = [...arguments].slice(1)

      //绑定参数 并执行函数
      const result = context[fnSym](...args) 

      //清除定义的this
      delete context[fnSym]

      // 返回结果 
      return result
    } 
    
    
    // call 如果能明白的话 apply其实就是改一下参数的问题
    // apply
    Function.prototype.MyApply = function (context) {
      if (typeof this !== "function") {
        throw new Error('type error')
      }

      if (context === null || context === undefined) {
        // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window
      } else {
        // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
        context = Object(context) 
      }


      // 使用Symbol 来确定唯一
      const fnSym = Symbol()
      //模拟对象的this指向
      context[fnSym] = this

      // 获取参数
      const args = [...arguments][1]

      //绑定参数 并执行函数 由于apply 传入的是一个数组 所以需要解构
      const result = arguments.length > 1 ? context[fnSym](...args) : context[fnSym]()

      //清除定义的this
      delete context[fnSym]

      // 返回结果  //清除定义的this
      return result
    }
    
    
    
    // bind
    Function.prototype.MyBind = function (context) {
      if (typeof this !== "function") {
        throw new Error('type error')
      }

      if (context === null || context === undefined) {
        // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window
      } else {
        // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
        context = Object(context) 
      }

      //模拟对象的this指向
      const self = this

      // 获取参数
      const args = [...arguments].slice(1)
        
      // 最后返回一个函数 并绑定 this 要考虑到使用new去调用,并且bind是可以传参的
      return function Fn(...newFnArgs) {
        if (this instanceof Fn) {
            return new self(...args, ...newFnArgs)
        }
            return self.apply(context, [...args, ...newFnArgs])
        }
    }
复制代码

7. 手写实现继承

这里我就只实现两种方法了,ES6之前的寄生组合式继承 和 ES6之后的class继承方式。

    /**
    * es6之前  寄生组合继承 
    */
    {
      function Parent(name) {
        this.name = name
        this.arr = [1, 2, 3]
      }

      Parent.prototype.say = () => {
        console.log('Hi');
      }

      function Child(name, age) {
        Parent.call(this, name)
        this.age = age
      }

      //  核心代码 通过Object.create创建新对象 子类 和 父类就会隔离
      // Object.create:创建一个新对象,使用现有的对象来提供新创建的对象的__proto__ 
      Child.prototype = Object.create(Parent.prototype)
      Child.prototype.constructor = Child
    }
    
    
    
    /**
    *   es6继承 使用关键字class
    */
     {
      class Parent {
        constructor(name) {
          this.name = name
          this.arr = [1, 2, 3]
        }
      }
      class Child extends Parent {
        constructor(name, age) {
          super(name)
          this.age = age
        }
      }
    }
复制代码

补充一个小知识, ES6的Class继承在通过 Babel 进行转换成ES5代码的时候 使用的就是 寄生组合式继承。

继承的方法有很多,记住上面这两种基本就可以了!

8. 手写 new 操作符

首先我们要知道 new一个对象的时候他发生了什么。

其实就是在内部生成了一个对象,然后把你的属性这些附加到这个对象上,最后再返回这个对象。

function myNew(fn, ...args) {
  // 基于原型链 创建一个新对象
  let newObj = Object.create(fn.prototype)

  // 添加属性到新对象上 并获取obj函数的结果
  let res = fn.call(newObj, ...args)

  // 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
  return res && typeof res === 'object' ? res : newObj;
}
复制代码

9. js执行机制 说出结果并说出why

这道题考察的是,js的任务执行流程,对宏任务和微任务的理解

console.log("start");

setTimeout(() => {
  console.log("setTimeout1");
}, 0);

(async function foo() {
  console.log("async 1");

  await asyncFunction();

  console.log("async2");

})().then(console.log("foo.then"));

async function asyncFunction() {
  console.log("asyncFunction");

  setTimeout(() => {
    console.log("setTimeout2");
  }, 0);

  new Promise((res) => {
    console.log("promise1");

    res("promise2");
  }).then(console.log);
}

console.log("end");
复制代码

提示:

  1. script标签算一个宏任务所以最开始就执行了
  2. async await 在await之后的代码都会被放到微任务队列中去

开始执行

  • 最开始碰到 console.log("start"); 直接执行并打印出 start
  • 往下走,遇到一个 setTimeout1 就放到宏任务队列
  • 碰到立即执行函数 foo, 打印出 async 1
  • 遇到 await 堵塞队列,先 执行await的函数
  • 执行 asyncFunction 函数, 打印出 asyncFunction
  • 遇到第二个 setTimeout2, 放到宏任务队列
  • new Promise 立即执行,打印出 promise1
  • 执行到 res("promise2") 函数调用,就是Promise.then。放到微任务队列
  • asyncFunction函数就执行完毕, 把后面的打印 async2 会放到微任务队列
  • 然后打印出立即执行函数的then方法  foo.then
  • 最后执行打印 end
  • 开始执行微任务的队列 打印出第一个 promise2
  • 然后打印第二个 async2
  • 微任务执行完毕,执行宏任务 打印第一个 setTimeout1
  • 执行第二个宏任务 打印 setTimeout2
  • 就此,函数执行完毕

画工不好,能理解到意思就行

标签:function,面试题,return,数组,getName,2022,new,手写,Foo
来源: https://www.cnblogs.com/zhang-a222/p/16212704.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有