前端技术百问

2019-10-09  本文已影响0人  飞跃疯人院_a

这也是个人去年面试准备的一些问题,有点效果吧,过了一个多小时的大厂一面,分享给大家。

问:如何理解html标签语义化?

问:css权重是什么?

问:盒模型有几种,它们区别是什么?

问:什么是BFC

问:如何触发BFC

问:你常用的清除浮动方式是什么?

.clear:after, .clear:before {
  content: ' ';
  display: table;
}
.clear:after {
  clear: both;
}

问:em、rem的区别?

问:不使用border属性画一条1px的线?

<div style='height: 1px; background: #666; overflow: hidden;'></div>

<hr size='1'></hr>

问:移动端1px问题?

box-shadow: 
  0  -1px 1px -1px #e5e5e5,   //上边线
  1px  0  1px -1px #e5e5e5,   //右边线
  0  1px  1px -1px #e5e5e5,   //下边线
  -1px 0  1px -1px #e5e5e5;   //左边线
  0 0 0 1px #e5e5e5;   //四条线

问:定位的方式有哪几种,它们的区别是什么?

问:垂直水平居中的实现方式有哪些?

问:你知道的左右宽度固定,中间自适应的三栏布局方案有哪些?

浮动:
.parent {overflow: hidden;}
.left {float: left; width: 100px;}
.right: {float: right; width: 100px;}
<div class='parent'>
  <div class='left'></div>
  <div class='right'></div>
  <div class='center'></div>
</div>

定位1:
.parent {postion: relative};
.left {position: absolute; left: 0; width: 100px};
.right {position: absolute; right: 0; width: 100px};
.center {postion: absolute; left: 100px; right: 100px};

定位2:
.parent {postion: relative};
.left {position: absolute; left: 0; width: 100px};
.right {position: absolute; right: 0; top: 0; width: 100px};
.center {margin: 0 100px 0 100px};

表格:
.parent {dispaly: table; width: 100%;}
.left {display: table-cell; width: 100px;}
.center {display: table-cell;}
.right {display: table-cell; width: 100px;}

弹性:
.parent {display: flex;}
.left {width: 100px;}
.center {flex: 1;}
.right {width: 100px;}

网格:
.parent {
  display: grid; 
  width: 100%; 
  grid-template-rows: 100px; 
  grid-template-columns: 100px auto 100px;
}

问:实现三个圆形的水平自适应布局?

难点在于高度的自适应
.parent {
  display: table;
  width: 100%;
}
.child {
  display: table-cell;
  padding-top: 33.33%;
  background: red;
  border-radius: 50%;
}

.parent {
  overflow: hidden;
}
.child {
  float: left;
  width: 33.33%;
  padding-top: 33.33%;
  border-radius: 50%;
  background: red;
}

.parent {
  display: flex;
}
.child {
  flex: 1;
  padding-top: 33.33%;
  border-radius: 50%;
  background: red;
}

问:介绍下flex布局?

主轴方向:水平排列(默认) | 水平反向排列 | 垂直排列 | 垂直反向排列
flex-direction: row | row-reverse | column | column-reverse;

换行:不换行(默认) | 换行 | 反向换行(第一行在最后面)
flex-wrap: nowrap | wrap | wrap-reverse;

flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap
flex-flow: <flex-direction> || <flex-wrap>;

主轴对齐方式:起点对齐(默认) | 终点对齐 | 居中对齐 | 两端对齐 | 分散对齐
justify-content: flex-start | flex-end | center | space-between | space-around;

交叉轴对齐方式:拉伸对齐(默认) | 起点对齐 | 终点对齐 | 居中对齐 | 第一行文字的基线对齐
align-items: stretch | flex-start | flex-end | center | baseline;

多根轴线对齐方式:拉伸对齐(默认) | 起点对齐 | 终点对齐 | 居中对齐 | 两端对齐 | 分散对齐
align-content: stretch | flex-start | flex-end | center | space-between | space-around;

问:JavaScript的变量有哪些类型?

问:基础类型和引用的区别?

问:函数参数是对象时会发生什么问题?

问:typeofinstanceof判断变量类型的区别?

问:有没有更好的判断变量类型的方法?

问:类数组转为数组的方式有哪些?

[].slice.call(arguments)
Array.from(arguments)
[...arguments]

问:如何判断一个变量是否是数组?

arr instanceof Array
Array.prototype.isPrototypeOf(arr)
Array.isArray(arr)
Object.prototype.toString.call(arr) === '[object Array]'
arr.constructor === Array

问:将多维数组扁平化?

function flatten(arr) {
  return [].concat(...arr.map(v => {
    return Array.isArray(v) ? flatten(v) : v;
  }))
}

function flatten(arr) {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, [])
}

function flatten(arr) {
  return arr.flat(Infinity);
}

function flatten(arr) {  // 纯数字
  return arr.toString().split(',').map(Number);
}

function flatten(arr) {
  const ret = [];
  while (arr.length) {
    const item = arr.shift();
    if (Array.isArray(item)) {
      arr.unshift(...item);
    } else {
      ret.push(item);
    }
  }
  return ret;
}

问:数组去重?

function unique(arr) {
  return [...new Set(arr)];
}

function unique(arr) {
  return arr.filter((v, i, a) => {
    return a.indexOf(v) === i;
  })
}

function unique(arr) {
  const tmp = new Map();
  return arr.filter(v => {
    return !tmp.has(v) && tmp.set(v);
  })
}

问:字符串的testmatchsearch它们之间的区别?

`test`是检测字符串是否匹配某个正则,返回布尔值;
/[a-z]/.test(1);  // false

`match`是返回检测字符匹配正则的数组结果集合,没有返回`null`;
'1AbC2d'.match(/[a-z]/ig);  // ['A', 'b', 'C', 'd']

`search`是返回正则匹配到的下标,没有返回`-1`。
'1AbC2d'.search(/[a-z]/);  // 2

问:字符串的slicesubstringsubstr它们之间的区别?

`slice`是返回字符串开始至结束下标减去开始下标个数的新字符串,下标是负数为倒数;
'abcdefg'.slice(2,3);  // c  // 3 - 2
'abcdefg'.slice(3,2);  // ''  // 2 - 3
'abcdefg'.slice(-2,-1);  // f  // -1 - -2

`substring`和`slice`正常截取字符串时相同,负数为0,且下标值小的为开始下标;
'abcdefg'.substring(2,3);  //c  // 3 - 2
'abcdefg'.substring(3,2);  // c  // 3 - 2 
'abcdefg'.substring(3,-3);  // abc  // 3 - 0

`substr`返回开始下标开始加第二个参数(不能为负数)个数的新字符串。
'abcdefg'.substr(2, 3);  // cde
'abcdefg'.substr(3, 2);  // de
'abcdefg'.substr(-3, 2); // ef

问:=====的区别?

问:是否===就完全靠谱?

问:在类型转换中哪些值会被转为true

问:谈谈对this的理解?

问:改变当前调用this的方式?

问:谈谈对闭包的理解?

问:谈谈对原型以及原型链的理解?

问:原型继承的方式有哪些?

function Parent(name) {
  this.name = name;
}
function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child

问:如何解决引用类型变量共享的问题?

问:函数防抖和节流的区别?

问:请手写一下map、call、new、instanceof、Events、深拷贝、节流、Promise等?

问:varletconst的区别 ?

问:SetWeakSet的区别?

const set = new Set();
const obj = {name: 'cc'};
set.add(obj);
obj = null;
[...set][0]; // {name: 'cc'} 转数组后依然可以访问到

const weakSet = new WeakSet();
const obj = {};
weakSet.add(obj);
obj = null;  // 会移除引用
weakSet.has(obj); // false

问:MapWeakMap的区别?

const obj = Object.create(null);
obj[1] = 'cc';
obj['1']; // cc

const map = new Map();
map.set(1, 'cc');
map.has('1');  // false   1 和 '1'不会被转换

问:箭头函数和普通函数的区别?

问:请实现plus(1)(2)(3)(4)等于8?

方法1:
function plus(n) {
  debugger
  let sum = n;
  const _plus = function (n) {
    sum += n;
    return _plus;
  };
  _plus.toString = function () {
    return sum;
  };
  return _plus;
}

方法2:
function multi() {
  const args = [].slice.call(arguments);
  const fn = function () {
    const newArgs = args.concat([].slice.call(arguments));
    return multi.apply(this, newArgs);
  }
  fn.toString = function () {
    return args.reduce(function (a, b) {
      return a + b;
    })
  }
  return fn;
}

问:谈谈对class的理解 ?

class Parent {
  constructor(name) {
    this.name = name;
  }
}
class Child extends Parent {
  constructor(name, age) {
    super(name);  // 相当于Parent.call(this, name)
    this.age = age;
  }
}

问:谈谈对Promise的理解 ?

问:谈谈对ES-Module的理解 ?

问:谈谈对Proxy的理解 ?

谈谈对Generator的理解?

问:谈谈对asyncawait的理解 ?

问:谈谈对Event-Loop的理解 ?

问:对浏览器或元素的各种距离参数你知道哪些?

问:怎么确定当前浏览器的类型?

问:什么是简单请求和复杂请求?

  1. 请求方法仅限getheadpost
  2. Content-type仅限text/plainmultipart/form-dataapplication/x-www-form-urlencoded

问:从输入域名到页面显示都经历了什么?

问:谈谈浏览器的渲染机制?

问:什么是重绘和回流?

问:如何减少重绘和回流?

问:什么是事件流/模型?

问:什么是事件代理?

问:什么是事件对象?

问:什么是跨域?

问:你知道的解决跨域的方式有几种?

问:cookiesession分别是什么?

问:cookiesession有什么不同?

问:为什么需要cookiesession

问:如果浏览器禁止了cookie怎么办?

问:使用cookie有哪些注意点?

问:前后端实现登录的方式有哪些?

问:浏览器实现本地存储的方式有哪几种?

问:了解Service Worker嘛?

问:谈谈对浏览器缓存的理解?

问:从哪些地方可以读取到浏览器缓存?

问:什么是浏览器缓存策略?

问:浏览器缓存适用的应用场景有哪些?

问:什么是XSS

问:如何防范XSS

问:什么是CSRF

问:如何防范CSRF

问:什么是点击劫持?

问:如何防范点击劫持?

问:什么是中间人攻击?

问:如果防范中间人攻击?

问:你知道的性能优化方式有哪些?

问:babel是如何将ES6代码转为ES5的?

问:有哪些方式可以提升webpack的打包速度?

问:有哪些方式可以减小webpack的打包后的体积?

问:对HTTP协议的理解?

问:什么是持久连接以及管线化?

问:为什么发起HTTP请求前需要TCP三次握手?

问:为什么关闭HTTP请求前需要TCP四次挥手?

问:HTTP请求报文和响应报文里分别有什么?

问:http1.0http1.1的区别?

  1. http1.1加如了持久连接。
  2. 加入了更多的请求头、响应头(缓存方面的)。
  3. 新增了一些错误状态码、如409(请求的资源和资源当前状态发生冲突)、410(服务器上的某个资源被永久性的删除)。

问:httphttps的区别?

url开头不一致是最明显的区分;其次http没有https安全,http没有经过SSL/TLS加密、身份验证;还有默认的端口不一样,http80https443https需要证书,https是防止中间人攻击方式的一种。

问:为什么https更安全?

  1. 公钥加密双方都需要事先知道一个都知道加密方式的密钥,优点是加密速度快,缺点是过程中可能会被窃取,安全性没有非对称加密高。
  2. 非对称加密会加入公钥和私钥,客户端使用第三方证书去服务端获取公钥,然后用获取到的公钥把共享密钥进行加密发送给服务端,服务端使用私钥解密出共享密钥,服务端用获取到的共享密钥进行消息的加密,客户端用用共享密钥进行解密。

问:常见的响应状态码有哪些?

问:get和post的区别?

问:什么是UDP协议?

问:谈谈vue初始化从数据到视图的过程,能详细些吗?

问:vue生命周期钩子有哪些,每个钩子阶段都做了什么?

问:组件之间通信方式有哪些?

问:父子组件如何完成数据双向绑定?

v-model形式: 只能绑定一个数据
父组件:
<template>
  <Child v-model='msg'/>  /* 可以使用value和@input的形式 */
</template>
export default {
  data() {
    return {
      msg: ''
    }
  }
}
子组件:
<template>
  <input v-model='currentMsg'/>
</template>
export default {
  props: ['value'],
  data() {
    return {
      currentMsg: this.value
    }
  },
  watch: {
    value(newVal) {
      this.currentMsg = newVal
    },
    currentMsg(newVal) {
      this.$emit('input', newVal)
    }
  }
}

sync形式: 绑定数据条数没限制
父组件:
<template>
  <Child :name.sync='name' :sex.sync='sex'/>
</template>
export default {
  data() {
    return {
      name: '',
      sex: ''
    }
  }
}
子组件:
<template>
  <div>
    <input v-model='currentName'/>
    <input v-model='currentSex'/>
  </div>
</template>
export default {
  props: ['name', 'sex'],
  data() {
    return {
      currentName: this.name,
      currentSex: this.sex
    }
  },
  watch: {
    name(newName) {
      this.currentName= newName
    },
    sex(newSex) {
      this.currentSex = newSex
    },
    currentName(newName) {
      this.$emit('update:name', newName)
    },
    currentSex(newSex) {
      this.$emit('update:sex', newSex)
    }
  }
}

问:什么是插槽、具名插槽、作用域插槽?

默认插槽:
<button>
  <slot>插槽默认属性</slot>
</button>

具名插槽:
<div>
  <slot name="head">Head</slot>
  <slot name="center">Center</slot>
  <slot name="footer">Footer</slot>
  <slot>Default</slot>
</div>

<TestComp>
  <template v-slot:head>
    <div>添加到head</div>
  </template>
  <template #center> // 简写
    <div>添加到center</div>
  </template>
  <template #footer>
    <div>添加到footer</div>
  </template>
  <template>添加到默认</template>
</TestComp>

作用域插槽:
<div>
  <slot :user="user">{{user.lastName}}</slot>
</div>

<TestComp v-slot="{user}">
  <div>{{user.firstName}}</div>
</TestComp>

具名插槽加作用域插槽
<div>
  <slot :user="user" name="info">{{user.lastName}}</slot>
</div>

<TestComp #info="{ user }">
  <div>{{user.firstName}}</div>
</TestComp>

问:v-showv-if 区别?

问:vue里数组绑定class的用法?

computed: {
  classes() {
    return [
      `${prefixCls}`, 
      {
        [`${prefixCls}-${this.type}`]: this.type !== ''
      }
    ]
  }
}

问:vue里的动画?

css动画钩子
1.v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
2.v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
3.v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
4.v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
5.v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
6.v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

JavaScript动画钩子
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
在 enter 和 leave 中必须使用 done 进行回调,否则,它们将被同步调用,过渡会立即完成。
appear会完成初始渲染。

问:如何实现一个自定义过滤器?

问:如何实现一个自定义指令?

问:谈谈对vuex的理解?

问:vuex语法糖方法有哪些以及如何使用?

computed: {
  ...mapState(['xxx', 'yyy'])
}

computed: {
  ...mapGetters(['xxx', 'yyy'])
}

methods: {
  ...mapMutations({
    zzz: 'XXX_YYY'
  })
}

methods: {
  ...mapActions(['yyy'])
}

问:vue-router如何传递参数?

方式1:
{
  path:"/home/:id",
  component:Home
}
this.$router.push({
  path:`/home/${id}`,
})
在Home组件中获取参数
this.$route.params.id

方式2:需要命名路由
{
  path:'/home',
  name: 'home'
  component:Home
}
this.$router.push({
  name:'home',
  params:{id:9527}
})
在Home组件中获取参数
this.$route.params.id

方式3:
{
  path:"/home",
  component:Home
}
this.$router.push({
  path:'/home',
  query:{id:9527}
})

在Home组件中获取参数
this.$route.query.id

问:vue-router有哪些导航守卫钩子?以及它们的执行顺序?

  1. beforeRouteLeave
  2. beforeEach
  3. beforeRouteUpdate
  4. beforeEnter
  5. beforeRouteEnter
  6. beforeResolve
  7. afterEach

问:如何实现异步组件?

方式1:
const Home= () => import('components/home')
export default new Router({
  routes: [
    {
      path: '/home',
      component: Home,
    },
  ]
})

方式2:可以指定多个路由为相同`chunk`名,会打包在一起
export default new Router({
  [{
    path: '/home',
    component: r => require.ensure([], () => r(require('../components/home')), 'home' /* chunk名 */)
  }]
})

方式3:
export default new Router({
  [{
    path: '/promisedemo',
    component: resolve =>  require(['../components/home'], resolve)
  }
]})

方式4:高级异步组件,带`loading`和`error`组件
const Home= () => lazyLoadView(import('components/home'))

export default new Router({
  routes: [
    {
      path: '/home',
      component: Home,
    },
  ]
})

function lazyLoadView(AsyncView) {
  const AsyncHandler = () => ({
    component: AsyncView,
    loading: require('@/components/loading').default,
    error: require('@/components/error').default,
    delay: 200,
    timeout: 10000
  });
  return Promise.resolve({
    functional: true,
    render(h, { data, children }) {
      return h(AsyncHandler, data, children);
    }
  });
}

问:请实现一个最小化vue响应式示例?

class Watcher {
  update() {
    console.log('更新~');
  }
}
class Dep {
  constructor() {
    this._watchers = new Set();
  }
  add(watcher) {
    if(!this._watchers.has(watcher)) {
      this._watchers.add(watcher);
    }
  }
  notify() {
    this._watchers.forEach(watch => {
      watch.update();
    })
  }
}

Dep.target = new Watcher();

function observer(target) {
  if (typeof target === 'object' && target !== null) {
    Object.keys(target).forEach(key => {
      defineReactive(target, key, target[key]);
    })
  }
}
function defineReactive(target, key, val) {
  const dep = new Dep();
  if (typeof val === 'object' && val !== null) {
    observer(val);
  }
  Object.defineProperty(target, key, {
    get() {
      dep.add(Dep.target);
      return val;
    },
    set(newVal) {
      dep.notify();
      val = newVal;
    }
  })
}

问:工厂模式?

function Coder(name, age) {
  this.name = name
  this.age = age
  this.career = 'coder'
  this.work = ['写代码', '写系分', '修Bug']
}
function ProductManager(name, age) {
  this.name = name
  this.age = age
  this.career = 'product manager'
  this.work = ['订会议室', '写PRD', '催更']
}
...
简单封装,不同再去一个个的new具体的角色
function Factory(name, age, career) {  
  if (career === 'coder') {
    return new Coder(name, age);
  } else if (career === 'product manager') {
    return new ProductManager(name, age);
  }
  ...
}

将角色抽象成User类,使用工厂进一步封装
function User(name, age, career, work) {
  this.name = name;
  this.age = age;
  this.career = career;
  this.work = work;
}
function Factory(name, age, career) {
  let work;
  if (career === 'coder') {
    word = ['写代码', '写细分', '修Bug'];
  } else if (career === 'product manager') {
    word = ['订会议室', '写PRD', '催更'];
  }
  ...
  return new User(name, age, career, word);
}

问:单例模式?

实现Storage类,使得该对象为单例,基于localStorage进行封装。实现方法 setItem(key,value) 和 getItem(key)?

静态方法版:
class Storage {
  static create() {
    if (!Storage.instance) {
      Storage.instance = new Storage();
    }
    return Storage.instance;
  }
  setItem(key, value) {
    return localStorage.setItem(key, value);
  }
  getItem(key, value) {
    return localStorage.getItem(key, value);
  }
}

闭包版:
function Storage() { }
Storage.prototype.setItem = function (key, value) {
  return localStorage.setItem(key, value);
}
Storage.prototype.getItem = function (key) {
  return localStorage.getItem(key, value);
}
const createStorage = (function () {
  let instance;
  return function () {
    if (!instance) {
      instance = new Storage();
    }
    return instance
  }
})()

问:实现一个全局唯一的模态框?

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>modal</title>
  <style>
    #modal {
      position: fixed; height: 200px; width: 200px;
      line-height: 200px; top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      border: 1px solid #000; text-align: center;
    }
  </style>
</head>

<body>
  <button id='open'>打开模态框</button>
  <button id='close'>关闭模态框</button>
  <script>
    const Model = (function () {
      let box;
      return function () {
        if (!box) {
          box = document.createElement('div');
          box.innerHTML = '唯一';
          box.id = 'modal';
          box.style.display = 'none';
          document.body.appendChild(box);
        }
        return box;
      }
    })()
    document.getElementById('open').addEventListener('click', () => {
      const model = Model();
      model.style.display = 'block';
    })
    document.getElementById('close').addEventListener('click', () => {
      const model = Model();
      model.style.display = 'none';
    })
  </script>
</body>
</html>

问:观察者模式和发布订阅模式的区别?

观察者模式:
class Subject {
  constructor() {
    this.observers = [];
  }
  add(observer) {
    this.observers.push(observer);
  }
  remove(observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }
  notify() {
    const obs = this.observers;
    for (let i = 0; i < obs.length; i++) {
      obs[i].update();
    }
  }
}
class Observer {
  constructor(name) {
    this.name = name;
  }
  update() {
    console.log('my name is' + this.name);
  }
}

发布订阅模式:
class Events {
  constructor() {
    this._evnets = Object.create(null);
  }
  
  on(event, fn) {  // 往事件中心添加事件
    if (Array.isArray(event)) {
      for (let i = 0; i < event.length; i++) {
        this.on(evnet[i], fn);
      }
    } else {
      (this._evnets[event] || (this._evnets[event] = [])).push(fn);
    }
  }
  
  emit(event, ...args) {  // 触发事件中心对应事件
    const cbs = this._evnets[event];
    if (cbs) {
      for (let i = 0; i < cbs.length; i++) {
        cbs[i].apply(this, args);
      }
    }
  }
  
  off(event, fn) {  // 移除事件
    if (!arguments) {
      this._evnets = Object.create(null);
      return this;
    }
    if (Array.isArray(event)) {
      for (let i = 0; i < event.length; i++) {
        this.off(event[i], fn);
      }
      return this;
    }
    if (!fn) {
      this._evnets[event] = null;
      return this;
    }
    const cbs = this._evnets[event];
    let i = cbs.length;
    while (i--) {
      const cb = cbs[i];
      if (cb === fn || cb.fn === fn) {
        cbs.splice(i, 1);
        break;
      }
    }
    return this;
  }
  
  once(evnet, fn) {  // 只执行一次
    function on() {
      this.off(evnet, on);
      fn.apply(this, arguments);
    }
    on.fn = fn;
    this.on(evnet, on);
    return this;
  }
}

分享一个笔者自己写的组件库,哪天可能会用的上了 ~ ↓

你可能会用的上的一个vue功能组件库,持续完善中...

上一篇 下一篇

猜你喜欢

热点阅读