记一次前端面试

396 阅读2分钟

基础相关

es6新增了哪些新特性,较之前有什么变化

  1. let变量,主要用于取代var
  • 阻止变量提升
console.log(a) // undefined
console.log(b) // Uncaught ReferenceError: Cannot access 'b' before initialization
var a
let b
  • 不可重复定义
let b = 2 // Uncaught SyntaxError: Identifier 'b' has already been declared
  • 只在块级作用域内有效
{
  let a = 1;
  {
    let a = 2;
    console.log(a);  // 2
  }
  console.log(a)  // 1
}
  1. const常量,一旦声明不可改变
const c = 1
c = 2 // Uncaught TypeError: Assignment to constant variable.
  1. 基本数据类型symbol,Symbol()函数会返回symbol类型的值
  • 每个从Symbol()返回的symbol值都是唯一的
Symbol("foo") === Symbol("foo"); // false
  1. 解构赋值
  • 数组解构
let foo = [1, 3, 5, 7, 9];
const [a, b, c, ...rest] = foo;
console.log(a); // 1
console.log(b); // 3
console.log(c); // 5
console.log(rest); // [7, 9]

// 剩余参数一定是要放在最后,否则报错
// const [a, ...rest, b, c] = foo; 
// Uncaught SyntaxError: Rest element must be last element
  • 默认值
let a, b;

[a=1, b=2] = [3];
console.log(a); // 3
console.log(b); // 2
  • 交换
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1
  • 对象解构,基本类似(键名需要保持一致)
let {a, b} = { a: 1, b: 2 }
  1. 模板字符串
let x = 110;
console.log(`请求呼叫${x}`);
  1. 箭头函数
  • 仅有一个参数时,可省略括号
const fn = param => {
	// ...
}
  • 函数体直接return一个结果,可省略 {}
const fn(a, b) => a + b;

了解防抖节流吗,什么场景下会用到

  1. 防抖
  • 在给定的n秒内再次触发,将会重新计时,到达n秒才执行函数
  • 搜索优化,停止输入达到n秒再执行检索
  • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
function debounce(fn, wait) {
    let timeout;
    return function(...args) {
        if(timeout) clearTimeout(timeout);
        timeout = setTimeout(() => {
            fn.apply(this, args);
        }, wait);
    }
}
  1. 节流
  • 就是指连续触发事件但是在 n 秒中只执行一次函数
  • 转盘抽奖,鼠标或手指持续触发的情况下
  • 监听滚动事件,比如是否滑到底部自动加载更多
function throttle(fn, wait) {
    let previous = 0;
    return function(...args) {
        let now = Date.now();
        if (now - previous > wait) {
            fn.apply(this, args);
            previous = now;
        }
    }
}

移动端适配,有哪几种方式

  1. rem,是基于根元素html计算的单位,1rem基本大小为16px
html {
	font-size: 2rem; // 32px
    div {
		font-size: 2rem; // 64px, 看到区别了吧
    }
}
  1. em,是基于父元素计算的单位
<div class="fir">
    <div class="sec">
        收到了3份offer
        <p class="thir">收到了3份offer</p>
    </div>
</div>
<style>
.fir {
    font-size: 20px;
}
.sec {
    font-size: 2em;
}
.thir {
    font-size: 2em;
}
</style>

  1. postcss-px-to-viewport(本人一般用这个插件)
// 安装
npm i postcss-px-to-viewport --save-dev

// vue.config.js中配置
module.exports = {
  // ...
  "postcss-px-to-viewport": {

    unitToConvert: "px", // 默认值`px`,需要转换的单位

    viewportWidth: 375, // 视窗的宽度,对应设计稿宽度

    viewportHeight: 667, // 视窗的高度, 根据375设备的宽度来指定,一般是667,也可不配置

    unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数

    propList: ["*"], // 转化为vw的属性列表

    viewportUnit: "vw", // 指定需要转换成视窗单位

    fontViewportUnit: "vw", // 字体使用的视窗单位

    selectorBlaskList: [".ignore-"], // 指定不需要转换为视窗单位的类

    mediaQuery: false, // 允许在媒体查询中转换`px`

    minPixelValue: 1, // 小于或等于`1px`时不转换为视窗单位

    replace: true, // 是否直接更换属性值而不添加备用属性

    exclude: [], // 忽略某些文件夹下的文件或特定文件

    landscape: false, // 是否添加根据landscapeWidth生成的媒体查询条件 @media (orientation: landscape)

    landscapeUnit: "vw", // 横屏时使用的单位

    landscapeWidth: 1134 // 横屏时使用的视窗宽度

  }
};

如何实现垂直水平居中

div
  width 100px
  height 100px
  position fixed
  top calc(50% - w/2)
  left calc(50% - h/2)

布局相关(太多,此处不展开)

  1. float属性
  2. flex

有做过两个独立的项目间,更新对方页面吗

木有

  • window.postMessage结合iframe可以实现
// test.html
<iframe src="http://192.168.xxx.xxx:8080/"></iframe>
let array= ['打工人', '打工魂', '打工都是人上人', '奥利给'];
window.onload = function() {
	window.frames[0].postMessage(array, '*');
}
// test.vue
mounted() {
  window.addEventListener('message', function(e) {
      console.log(e.data, '------fromOtherProj')
  })
}

深拷贝怎么实现

  • 主要就是递归遍历自身
function deepClone(source) {
    let objClone = Array.isArray(source) ? [] : {};
    if (source && typeof source === 'object') {
        for (const key in source) {
            if (source.hasOwnProperty(key)) {
                if (typeof source[key] === 'object') {
                    objClone[key] = deepClone(source[key]);
                } else {
                    objClone[key] = source[key]
                }
            }
        }
    }
    return objClone;
}

求一个元素在数组中出现的次数

  • 当时用的比较菜的方式,然后面试官要求改进时间复杂度为O(1),不能用for循环了(这我还没研究,有路过的可以指点下)
function getLength(array, x) {
    let tmp = []
    array.forEach(element => {
        if (element === x) {
           tmp.push(element) 
        }
    });
    return tmp.length;
}

了解有哪些前端攻击吗

  • 之前小公司经历过一个xss跨站脚本攻击(收到非法图片url)
  • 主要原因是过分信任客户端传输信息,导致有恶意传输脚本(常见iframe,script标签)变成了程序可执行的一部分,可能导致账户信息、劫持回话、弹框登录(恶意引导至非法页面)、改变内容欺骗用户等后果
  • 避免xss攻击也简单,前端进行表单验证,格式校验,服务端给用户增加token,前端登录时携带token,post请求取代get请求

js中类的继承是怎么实现的

  • 根据《你不知道的JavaScript》系列讲述,js中其实没有类的概念,‘类’的继承是基于原型(prototype)委托

讲讲原型和原型链吧

  1. 原型
  • 对象的原型就是其构造函数的prototype
  • 引用类型有一个__proto__属性,称之为隐式原型,该属性又指向其构造函数的prototype
  1. 原型链
  • 原型链不是一个结构的概念
  • 是在查找对象属性的时候,当前对象本身没有找到,就会先去到它的隐式原型(person1.__proto__)上查找,对应其构造函数的原型(Person.prototype),还没有找到就会继续向上,Person.prototype.__proto__,对应其构造函数的原型(Object.prototype),如果还没找到,直到Object.prototype.__proto__(指向null)
  • 这样一层一层查找的过程,称为原型链

instanceof实现

  • instanceof是基于原型,只能用来判断引用类型
function instanceof(obj, type) {
	if(typeof obj !== 'object' || obj === null) return false;
    let proto = obj.__proto__;
    let prototype = type.prototype;
    while(true) {
    	if(proto === null) return false;
        if(proto === prototype) return true;
        proto = proto.__proto__;
    }
}

vue相关

如何实现v-model双向绑定

  • 父组件
<template>
    <div>
      <p>{{ value }}</p>
      <son v-model="value" />
    </div>
</template>

<script>
import son from './son.vue'
export default {
    data() {
        return {
          value: '',
        };
    }
    components: {
      son
    }
};
</script>
  • 子组件
<template>
    <div>
      <input :value="value" @input="onInput" />
    </div>
</template>

<script>
export default {
    model: { // 如果不使用model属性,$emit('input', xxx)也是一样的
      prop: 'value',
      event: 'change'
    },
    props: {
      value: {
        type: String,
        default() {
          return ''
        }
      }
    },
    methods: {
      onInput(e) {
        this.$emit('change', e.target.value)
      }
    }
};
</script>

vue是如何实现双向绑定的

  1. vue2.x版本是基于Object.defineProperty(),通过其内部set和get方法,对数据进行劫持
  2. vue3.0是基于Proxy()来实现的

vue页面优化

  1. v-if和v-show区分场景使用
  • 频繁切换,则需要提前渲染好,控制display显隐即可,适用v-show
  • 不涉及频繁切换,则可以以使用v-if
  1. v-for遍历,加 :key="xxx",提高性能(diff算法进行父子节点更新的时候基于该key)
  2. 针对纯展示列表,axios请求过后进行this.list = Object.freeze(data),冻结对象,禁止双向绑定修改对象,减少一定的性能消耗
  3. 使用vant等UI库,使用babel-plugin-import、babel-plugin-component等插件按需引入,而不是使用全部引入,减少包体积
  4. 大量图片使用vue-lazyload懒加载等插件
  5. 路由懒加载,也是常规习惯
  6. 骨架/呼吸屏,提升用户体验
  7. SSR服务端渲染,相当于服务端直接返回html页面,提升首屏加载速度
  8. 架构层面,前端微服务,之前公司使用docker+caas平台,特别对于APP内嵌的情况,分模块各自部署在独立的微服务,减轻服务器压力,同时可以无感更新,提升用户体验

组件通讯都有哪些方式

  1. 父子组件通讯
  • 父向子:父组件中给子组件标签v-bind绑定属性,子组件props接收
  • 子向父:父组件中给子组件标签绑定事件(订阅事件),子组件$emit('xxx', params)(发布事件)
  • provide、inject
  • $parent、$children
  1. eventBus总线机制,可跨组件级别,兄弟组件,爷俩组件都ok的
  2. Vue.observable()对象,简版的状态管理,创建之后,可随处更改state中的变量,也可随处获取state变量
  3. vuex核心插件,state,getters,mutations,actions4大属性,同时可做module拆分,mutations是同步提交,actions可异步提交多个dispatch到mutations

vue-router和vuex项目中怎么封装使用的

  • 本人博客哈

vue-router

vuex

用过react吗,vue和react的相似和区别之处

木有

  1. 相似
  • virtual dom,简单说就是用js对象格式来描述dom
  • 组件化思想
  • props子组件向父组件传值
  • 脚手架,vue-cli和Create React App
  • 配套核心插件,vuex,vue-router 和 react-redux,react-router
  1. 区别
  • vue是双向绑定,react是单向数据流
  • vue默认是模板语法(也支持render和JSX语法),react默认是JSX(JavaScript和xml语法混合着写)

写在最后

  1. 面试的时候会的好好说,要有条理,不知道的更要理直气壮,这个说不好不会,就行了
  2. 别怕打击,被嫌弃太菜,下一家接着面,毕竟各位都是仌
  3. 每场面试都是一次学习机会,也是对自己技术的检验,珍惜