前端面试题

183 阅读9分钟

知识点篇

1、call、apply、bind的区别

call:obj.myFun.call(db,'成都', ... ,'string' )。

apply:obj.myFun.apply(db,['成都', ... ,'string'] )。

bind:obj.myFun.bind(db,'成都', ... ,'string' )()。

bind 返回的是一个新的函数,你必须调用它才会被执行。

obj.myFun.call(db,'成都','上海'); // 德玛 年龄 99  来自 成都去往上海
obj.myFun.apply(db,['成都','上海']);  // 德玛 年龄 99  来自 成都去往上海  
obj.myFun.bind(db,'成都','上海')();   // 德玛 年龄 99  来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])();   // 德玛 年龄 99  来自 成都, 上海去往 undefined

2、删除指定位置的字符串

'1234567'.replace('2','')      // 134567
'12345678'.split('2').join('')      //134567

3、for...in 和 for... of的区别:

1、for...in 循环出的是key值,for...of循环的是value值

2、for...in 推荐在循环对象属性的时候使用,for...of推荐在遍历数组的时候使用

3、for...of 不能循环普通的对象,需要通过和Object.keys()搭配使用

4、for..in 会继承原型链上的属性,需要配合hasOwnProperty来枚举自己的属性

4、for循环

break 退出本轮循环

continue 退出本此循环 继续执行下一次循环

5、forEach和map的区别:

map循环一个数组,返回一个值组成新的数组,原数组不会改变

forEach只是循环数组,无返回值,undefined

6、截取字符串中的字符

substr(开始的索引值,length) :返回一个从指定位置开始的指定长度的子字符串。

'12345678'.substr(1,5)   //23456

substring(开始的索引值,结束的索引值):返回位于String对象中指定位置的子字符串。

7、数组去重的方法

第一种

var arr=[1,2,2,3,5,6,7,5,5,4,6];   
var arr1=[];
for(var i = 0; i < arr.length; i++){
    if (arr1.indexOf(arr) == -1){
		arr1.push(arr)};
	}
console.log(arr); //1,2,3,5,6,7,4

第二种:

function unique(arr) {
   //Set数据结构,它类似于数组,其成员的值都是唯一的
   return Array.from(new Set(arr)); // 利用Array.from将Set结构转换成数组
}
unique([0,0,1,1,2,3,4,4]);  // [0, 1, 2, 3, 4]

第三种:

var arr = [1, 3, 4, 1, 5, 2, 6, 7, 8, 3]
var arrN = arr .filter((item, index, self) => self.indexOf(item) === index)
console.log(arrN ) //[1, 3, 4, 5, 2, 6, 7, 8]

第四种:

var newArr = arr.reduce(function (prev, cur) {
    prev.indexOf(cur) === -1 && prev.push(cur);
    return prev;
},[]);

8、闭包

详情见:juejin.cn/post/687632…

闭包:有权访问另一个函数作用于中的变量的函数

闭包会发生内存泄漏,每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址 让外界读取某个函数内部的变量,让这些变量始终保持在内存中(可能会造成内存泄漏)

最基本的作用:可以通过闭包返回的函数或者方法,来修改函数内部的数据

创建一个私有的空间,保护数据

9、三次握手

为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端

握手之前主动打开连接的客户端结束closed阶段,被动打开的服务器端也结束closed阶段,并进入listen阶段。随后开始“三次握手”:

1.首先客户端向服务器端发送一段TCP报文,标记位为SYN,表示“请求建立新连接”,随后客户端进入SYN-SENT阶段。

2.服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,标志位为SYN和ACK,表示“确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);

3客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:标志位为ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);随后客户端进入ESTABLISHED阶段。

10、子盒子在父盒子内垂直居中:

1、子绝父相,

子盒子设置top:50%,left:50%;
margin-top:盒子高度的一半; margin-left: 盒子宽度的一半; 
或者 transform:translate(-50%,-50%)

2、flex布局

父盒子: display: flex;   justify-content: center;  align-items: center;

3、子绝父相 + position

子绝父相,子盒子设置
position: absolute; 
top: 0;
left: 0;
bottom: 0;
right: 0;
ååmargin: auto;

11、浏览器缓存

分为强缓存和协商缓存

强缓存包括:Expires 和 Cache-Control。

协商缓存包括:last-modified/last-modiied-since和tage/if-none-match

12、CSRF和XSS

XSS:恶意攻击者往web页面中插入恶意的script代码,当用户浏览该页时,嵌入其中的script代码会被执行,从而达到恶意攻击用户的目的

CSRF:攻击者借助受害者的Cookie骗取服务器的信任,可以在受害者毫不知情的情况下以受害者的名义伪造请求发送给受攻击服务器,从而在未授权的情况下执行在权限保护之下的操作

13、说说你对SSR的了解

SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端

SSR的优势

1、更好的SEO
2、首屏加载速度更快

SSR的缺点

开发条件会受到限制,服务器端渲染只支持beforeCreate和created两个钩子 当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境 更多的服务端负载

14、你都做过哪些Vue的性能优化?

1、编码方面

1、尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher。 2、v-if和v-for不能连用,如果需要使用v-for给每项元素绑定事件时使用事件代理 3、SPA 页面采用keep-alive缓存组件 4、在更多的情况下,使用v-if替代v-show 5、key保证唯一 6、使用路由懒加载、异步组件 7、防抖、节流 8、第三方模块按需导入 9、长列表滚动到可视区域动态加载 10、图片懒加载

2、SEO优化

预渲染和服务端渲染SSR

3、打包优化

1、压缩代码 2、Tree Shaking/Scope Hoisting 3、使用cdn加载第三方模块 4、多线程打包happypack 5、splitChunks抽离公共文件 6、sourceMap优化

4、用户体验

1、骨架屏 2、PWA 3、还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

15、说说你对 SPA 单页面的理解,它的优缺点分别是什么?

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

优点:

用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染; 基于上面一点,SPA 相对对服务器压力小; 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;

缺点:

初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;

前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;

SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。

16.&& 与 ||

a && b: a和b同时为true 才返回 true, 否则返回false;

a || b:a或b任意一个为true 就返回true , 否则返回false

手写代码篇

1、防抖

在短时间内多次触发同一个函数,只执行最后一次,或者只在开始时执行

// debounce 函数接受一个函数和延迟执行的时间作为参数
function debounce(fn, delay){
    // 维护一个 timer
    let timer = null;
    return function() {
        // 获取函数的作用域和变量
        let context = this;
        let args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function(){
            fn.apply(context, args);
        }, delay)
    }
}
function foo() {
  console.log('trigger');
}
// 在 debounce 中包装我们的函数,过 2 秒触发一次
window.addEventListener('resize', debounce(foo, 2000));

2、节流

节流是在一段时间内只允许函数执行一次

var throttle = function(func, delay){
    var timer = null;
    return function(){
        var context = this;
        var args = arguments;
        if(!timer){
            timer = setTimeout(function(){
                func.apply(context, args);
                timer = null;
            },delay);
        }
    }
}

3、new

function myNew(Func) {
  const instance = {};
  if (Func.prototype) {
    Object.setPrototypeOf(instance, Func.prototype);
  }
  const res = Func.apply(instance, [].slice.call(arguments, 1));
  if (typeof res === "function" || (typeof res === "object" && res !== null){
    return res;
  }
  return instance;
}

4、bind

Function.prototype.myBind = function (context = globalThis) {
  const fn = this;
  const args = Array.from(arguments).slice(1);
  const newFunc = function () {
    if (this instanceof newFunc) {
      // 通过 new 调用,绑定 this 为实例对象
      fn.apply(this, args);
    } else {
      // 通过普通函数形式调用,绑定 context
      fn.apply(context, args);
    }
  };
  // 支持 new 调用方式
  newFunc.prototype = fn.prototype;
  return newFunc;
};

5、深拷贝

function deepCopy(obj, cache = new WeakMap()) {
  if (!obj instanceof Object) return obj;
  // 防止循环引用
  if (cache.get(obj)) return cache.get(obj);
  // 支持函数
  if (obj instanceof Function) {
    return function () {
      obj.apply(this, arguments);
    };
  }
  // 支持日期
  if (obj instanceof Date) return new Date(obj);
  // 支持正则对象
  if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
  // 还可以增加其他对象,比如:Map, Set等,根据情况判断增加即可,面试点到为止就可以了

  // 数组是 key 为数字素银的特殊对象
  const res = Array.isArray(obj) ? [] : {};
  // 缓存 copy 的对象,用于出来循环引用的情况
  cache.set(obj, res);

  Object.keys(obj).forEach((key) => {
    if (obj[key] instanceof Object) {
      res[key] = deepCopy(obj[key], cache);
    } else {
      res[key] = obj[key];
    }
  });
  return res;
}

6、promise

// 建议阅读 [Promises/A+ 标准](https://promisesaplus.com/)
class MyPromise {
  constructor(func) {
    this.status = 'pending'
    this.value = null
    this.resolvedTasks = []
    this.rejectedTasks = []
    this._resolve = this._resolve.bind(this)
    this._reject = this._reject.bind(this)
    try {
      func(this._resolve, this._reject)
    } catch (error) {
      this._reject(error)
    }
  }

  _resolve(value) {
    setTimeout(() => {
      this.status = 'fulfilled'
      this.value = value
      this.resolvedTasks.forEach(t => t(value))
    })
  }

  _reject(reason) {
    setTimeout(() => {
      this.status = 'reject'
      this.value = reason
      this.rejectedTasks.forEach(t => t(reason))
    })
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this.resolvedTasks.push((value) => {
        try {
          const res = onFulfilled(value)
          if (res instanceof MyPromise) {
            res.then(resolve, reject)
          } else {
            resolve(res)
          }
        } catch (error) {
          reject(error)
        }
      })
      this.rejectedTasks.push((value) => {
        try {
          const res = onRejected(value)
          if (res instanceof MyPromise) {
            res.then(resolve, reject)
          } else {
            reject(res)
          }
        } catch (error) {
          reject(error)
        }
      })
    })
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }
}

// 测试
new MyPromise((resolve) => {
  setTimeout(() => {
    resolve(1);
  }, 500);
})
  .then((res) => {
    console.log(res);
    return new MyPromise((resolve) => {
      setTimeout(() => {
        resolve(2);
      }, 500);
    });
  })
  .then((res) => {
    console.log(res);
  }, err => {
    console.log('==>', err);
  });