为了账号安全,请及时绑定邮箱和手机立即绑定

【面试趣谈】前端多次面试常见题目(必考必会)

手写面试题

深拷贝

在JavaScript中我们对引用类型进行赋值时,我们会发现无论当我们修改赋值前的值还是赋值后的值,两个值都会发生改变。
例子如下:

let test = {
	course: 'web前端'
}
let newTest = test
test.course = '测试'
console.log('test', test)
console.log('newTest', newTest)

我们发现控制台输出的testnewTest的值是一样的,结果如下:

test {course: '测试'}
newTest {course: '测试'}

这是因为我们虽然对test.course进行了重新赋值,但是因为对象时存储在堆内存中的,所以testnewTest会指向同一个内存地址,导致两个值的来源是同一个地方。

为了解决上面拷贝的问题,我们可以手动实现一个深拷贝。

我们使用递归的方式实现深拷贝

var obj = {
    address: {
        city: 'hefei'
    },
    arr: ['a', 'b', 'c']
}

const deepObj = deepClone(obj)

deepObj.address.city = 'fuyang'
console.log(obj.address.city)
console.log('obj', obj)
console.log(deepObj)

// 深拷贝
function deepClone(obj = {})  {
  if (typeof obj !== 'object' || obj == null) {
    return obj
  }
  let result
  if (obj instanceof Array) {
    result = []
  } else {
    result = {}
  }
  for (let key in obj) {
    // 保证 key 不是原型的属性
    if (obj.hasOwnProperty(key)) {
      // 递归调用!!!
      result[key] = deepClone(obj[key])
    }
  }
  // 返回结果
  return result
}

控制台结果如下:
图片描述

我们还可以使用JSON中的stringify和parse方法来实现深拷贝

var obj = {
	age: 55,
    address: {
        city: 'hefei'
    }
}

const deepObj = deepClone(obj)
function deepClone(obj) {
  let newObj = JSON.stringify(obj)
  let newObj1 = JSON.parse(newObj)
  return newObj1
}
obj.age = 66
console.log('obj',obj);
console.log('deepObj',deepObj);

控制台输出结果如下:
图片描述

如果想要快速实现深拷贝,我们可以JSON的方式,来回转一下就可以实现。

防抖

例如我们在输入框中输入内容,输入时会向后端请求数据接口,如果我们不进行防抖操作,那么我们每次输入的时候都会进行数据请求,这样不仅浪费性能而且会对浏览器造成压力。

实现防抖结果如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <input type="text">
  <script>
    let inpCont = document.querySelector('input');
    
    inpCont.oninput = debounce(function() {
      console.log(this.value);
    }, 1000)

    function debounce(fn, delay) {
	   // timer是用来存放定时器的返回值
      let timer = null
      return function() {
        if (timer !== null) {
	      // 每当用户输入的时候我们就会把前一个 setTimeout 进行清除
          clearTimeout(timer)
        }
        // 创建一个新的 setTimeout ,可以保证输入字符后的延迟时间内,如果还有字符输入的话, 就不会执行 fn 函数
        timer = setTimeout(() => {
          fn.call(this);
        }, delay)
      }
    }
  </script>
</body>
</html>

原理就是当我们输入这个事件时,就会触发延时,如果我们在延迟时间内再次触发事件时,就会销毁前面的延迟并重新计算延时。如果在延迟时间内没有触发事件,那么就执行处理函数。

节流

当我们滚动滚动条会不断触发滚动函数中的值或者文章在编辑过程中每一段时间进行自动保存都会用到节流,防止事件高频率触发。

实现方法如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      height: 1800px;
    }
  </style>
</head>
<body>
  <script>
    window.onscroll = throttle(function() {
      console.log('滚动值11');
    }, 1000)

    function throttle(fn, delay) {
	   // 通过闭包保存一个标记,防止全局都可以进行访问该变量
      let status = true
      return function() {
        if (status) {
	       // 将外部传入的函数的执行放在 setTimeout 中
          setTimeout(() => {
            fn.call(this)
            // 最后在 setTimeout 执行完成后,我们把标记设置为true时,就可以执行下一次循环了。
            status = true
          }, delay)
        }
        status = false
      }
    }
  </script>
</body>
</html>

无论是防抖还是节流,我们都需要改变this的指向,不然的话,不改变的话,我们会发现this指向的是window,而不是我们需要指向的内容。

call、apply、bind

call是一个函数的方法,可以用来调用函数。

function people() {
	// this指向的是window
	console.log(this)
	console.log('people的内容')
}
people()

此时代码中的this指向的是window,因为people()相当于是window.people()。所以this指向的是window

如果我们定义一个param,让people去调用。如下

function people() {
	// this指向的是window
	console.log(this.age)
	console.log('people的内容')
}
let param = {
	age: 27
}
people.call(param)

如果我们不是用call改变this,会发现输出的是undefined而不是param里面的age内容。

我们除了改变this,还可以传参,如果需要的参数不传的话,会显示undefined

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    let param = {
      age: 27,
      action(run) {
        console.log('我每天都' + run)
      }
    }
    let test = {
      age: 25
    }
    param.action.call(test)
  </script>
</body>
</html>

此时上面的代码输出的是undefined,因为我们没有传参。所以我们需要传参。如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    let param = {
      age: 27,
      action(run) {
        console.log('我每天都' + run)
      }
    }
    let test = {
      age: 25
    }
    param.action.call(test, '健身')
  </script>
</body>
</html>

不同之处

applycall不同之处就在于传参的类型不同,call的传参是直接写在第一个参数后面就行,而apply的参数是放在数组里面的。bindcall的写法上是一样的,但是bind返回的是一个函数并不会执行函数,而call会执行函数。

param.action.apply(test, ['健身']);
param.action.call(test, '健身');
// 等价于
let litt = param.action.call(test, '健身');
litt()

用处

我们可以使用call去实现一个继承。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    function Parent() {
      this.age = function() {
        console.log('我今年27岁了')
      }
    }
    function People() {}
    let people = new People()
    people.age();
  </script>
</body>
</html>

此时上面的代码输出的是错误,因为people没有age方法。而Parent中有age方法,所以我们就需要让people会继承Parent中的age方法。使用call来解决,如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    function Parent() {
      this.age = function() {
        console.log('我今年27岁了')
      }
    }
    function People() {
      Parent.call(this)
    }
    let people = new People()
    people.age();
  </script>
</body>
</html>

我们在People中使用Parent.call(this)可以让People继承Parentage方法。从而输出age里面的内容了。

点击查看更多内容
7人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消