面试复盘

继承

继承就是要把 A 的构造函数属性与原型上的方法继承到 B 的构造函数上去。

继承的方式

  1. 类继承

将父类的实例指向子类的 prototype 属性

function SuperClass() {
  this.superObj = {};
}
SuperClass.prototype.superMethod = function() {};

function SubClass() {}
SubClass.prototype.subMethod = function() {};

SubClass.prototype = new SuperClass();
const sub = new SubClass();
  1. 构造函数继承

使用 apply, call 改变函数的执行上下文,在子类中调用父类的构造函数,就相当于以子类的 this 调用父类的构造函数。

function SuperClass() {
  this.superObj = {};
}
SuperClass.prototype.superMethod = function() {};

function SubClass(args) {
  // 继承父类
  SuperClass.apply(this, args);
}
SubClass.prototype.subMethod = function() {};

const sub = new SubClass();
  1. 寄生组合式继承

B 继承 A

  • 在构造函数 B 中,使用 apply 或 call 更改函数执行上下文,直接调用构造函数 A
  • Object.create(A.prototype) 新建一个原型,复制到构造函数 B 的原型上。
  • 需要修改构造函数 B 上原型上的构造函数,要执行其本身
function SuperClass() {
  this.superObj = {};
}
SuperClass.prototype.superMethod = function() {};

function SubClass(args) {
  SuperClass.apply(this, args);
}
SubClass.prototype.subMethod = function() {};

SubClass.prototype = Object.create(SuperClass.prototype);
SubClass.prototype.constructor = SubClass;

const sub = new SubClass();

专业一点来说

  1. 在子类构造函数中,用 call, apply 调用父类构造函数。就能继承父类的属性和方法。
  2. 用 Object.create(), 以父类的 prototype 属性作为原型,生成子类的 prototype 属性。这样就能继承父类原型对象的属性和方法。
  3. 更改子类构造函数的指向
  1. ES6 class/extends
class SuperClass {
  constructor() {
    this.superMethod = function() {};
  }
}

class SubClass extends SuperClass {
  constructor() {
    super();
  }
}

const sub = new SubClass();

例子

// es5
function Person(name, age) {
  this.name = name;
  this.age = age;

  // 公有方法
  this.sayHi = function() {
    console.log(`Hi, this is ${this.name}`);
  };
}
// Person 原型上独有的方法
Person.prototype.fn = () => {
  console.log('Person prototype 运行的方法!!');
};

function Student(name, age) {
  Person.apply(this, arguments);
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.fn = () => {
  console.log('Student prototype 运行的方法~~~');
};

const p = new Person('kk', 16);
const s = new Student('bb', 7);

console.log(p, s);

// ES6
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;

    // 公有方法
    this.sayHi = function() {
      console.log(`Hi, this is ${this.name}`);
    };
  }
  fn() {
    console.log('Person prototype 运行的方法!!');
  }
}

class Student extends Person {
  fn() {
    console.log('Student prototype 运行的方法~~~');
  }
}

const p = new Person('kk', 16);
const s = new Student('bb', 7);

console.log(p, s);

Promise

Promise 是用来解决浏览器异步操作,可以做链式调用,用来处理回调地狱。

Promise 有三种状态,pending, fulfilled, rejected。一旦状态从 pending 发生改变后,状态就不会再改变。

Promise 对象接受一个回调函数作为参数,回调函数接受 resolve 和 reject。

Promise 提供了好几个方法:

  1. Promise.finally() 不管是 Promise 状态如何都会执行,该方法的回调不接受任何参数

  2. Promise.all() 将多个 Promise 实例,包装成一个新的 Promise 实例。同时成功和失败的返回值是不一样的。成功时,返回的是一个结果数组,失败时,返回最先被 reject 状态的值。 Promise.all() 用来处理多个异步时,特别有用。Promise.all([async1, async2, async3]), 是按照顺序发起的,他们是异步的,互不阻塞。等所有任务结束后,他们的结果仍然是按顺序映射到 resultList 里面。

  3. Promise.race() 就是那个结果获得的快,就返回哪个结果。不管是成功还是失败。

ES6 数组常用到的有哪些

  • filter
  • map
  • forEach
  • reduce
  • fill
  • flat
  • flatMap
  • includes
  • entries, keys, values 返回的是遍历器对象,用 next().value 可以拿到值

移动端遇到的问题

预编译的优点

  • 选择器可嵌套
  • 变量
  • 让 css 可编程
  • mixin 混入
@mixin bg-color {
  background-color: red;
}

.div {
  @include bg-color;
}

webpack 的处理流程

首先 entry 和 output 是一条主线,entry 进去要解析文件,如果遇到不是 js 文件的话,那么就需要用 loader 加载成 js 处理。因为 webpack 只能处理 js 文件。 如果想要在各个生命周期做处理,就用 plugin 去钩住对应的生命周期,最后根据 output 导出对应的文件。

编译流程open in new window构建流程open in new window深入理解 webpack 原理 (上)open in new window深入理解 webpack 原理 (下)open in new window

vue 按需加载

  1. 在没有 babel 之前,用的是 node 的 require.ensure()

  2. 其实就是用了 babel 的动态引入的插件,将原来的 import xxx from xxx 的形式,改成用 import() 方式导入。

思考怎么异步加载 js?

在 script 标签上加上,async 或者 defer。

get 和 post 的区别

  1. get 用来获取数据,post 用来提交数据

  2. 传输方式:get 是通过 url 传输,post 是通过 Request body 传输

  3. 请求参数:get 有非 ASCII 字符则需要序列化, post 不需要。 get 参数会被保留在浏览器的历史记录中,post 不会

  4. get 会被浏览器主动 cache, post 不会,需要手动设置

  5. 传输长度:get 参数有长度限制(浏览器限制 2k),post 则没有

  6. get 的安全性没有 post 的安全性高

  7. get 在浏览器回退时是无害的,而 post 会重新发起请求

  8. get 产生一个 TCP 数据包,浏览器会把 header 和 data 一并发送,服务器返回 200 则 ok;而 POST 产生两个数据包,是先发送 header,服务器返回 100 continue,再继续发送 data,服务器响应 200 ok;

在网络环境好的情况, 发一次包和发两次包所需的时间差别不大。但在网络差的情况下,两次包的 TCP 在验证数据完整性上,有非常大的优点。 但并不是所有浏览器都发送两次,firefox 只发送一次。

扩展知识点: put 与 post 的区别

  • put 修改数据,如果两个请求相同,后一个请求会把前一个请求覆盖掉;
  • post 则不会

vue 3.0 有哪些特点

  1. Object.defineProperty 改成 Proxy,可以监听到数组与对象的改变。

  2. 优化插槽生成,分离渲染

  3. 现在变成 composition Api,以前是 function Api, setup 是 composition 的入口。

  4. template 模板中,2.0 只能有一个根标签,3.0 可以有多个根标签,或者也可以使用空标签<Fragment>

  5. 生命周期,现在周期都叫 onXXX, 以前的 beforeCreate 和 created 都改成了 setup, 其他的 onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured. 新增了调试了的生命周期,onRenderTracked, onRenderTriggered.

promise 与 async+await 的区别

  1. promise 就是表示一个异步操作后最终得到的状态值和结果值。

  2. async+await 是基于 promise + generator 封装的一个语法糖。

vue 生命周期的作用?

作者提供给用户,在不同的生命周期做用户想要做的的事情。

dom 渲染是在哪个周期完成的?

mounted

vue-router,hash 和 history 模式

  1. url 上 hash 模式会夹杂着#号, 而 history 则没有。

  2. hash 模式依靠 onhashchange 事件监听 location.hash 的变化,而 history 模式,是通过 HTML5 新增的两个方法,在不刷新的情况下,操作浏览器的历史记录。history.popState()新增历史记录、history.replaceState() 替换历史记录。

  3. history 模式上,url 刷新的时候,需要服务器配合,因为前端会发送请求,此时的地址,如果服务器找不到的会返回 404,所以需要后台好地址。可以配置 nginx,让未知地址,都转发到主页,主页会加载 vue.js,这样就会 vue.router,vue.route 会读取浏览器的 url 地址,这样就能成功跳转了。

  4. hash 模式不利于 SEO 优化

vue ref 的作用

  1. 获取 dom 元素
  2. 父子组件之间,可以通过 ref 拿到子组件的 data,与调用子组件的方法

vuex action 为什么要用异步的

因为更改 state 的函数必须是纯函数,纯函数即统一输入就会统一输出,没有副作用; 如果异步则会引入额外的副作用,导致更改后的 state 不可预测;

所以统一由 action 做操作,然后 commit 给 mutations,mutations 再去改变 state。

父子之间传值,除了用 props,$emit,还能用什么方式

  1. v-model
  2. eventBus 事件循环机制 实现事件的监听、派发和回调
  3. vuex
  4. provide()和 inject() 但是这个不是响应式的数据

css 选择器的优先级

内联 > ID 选择器 > 类选择器 > 标签选择器 !important 优先级最高

vuex 状态管理

组件传参 A->B->C->D A->D 有几种方式

  1. eventBus
  2. vuex
  3. provide/inject
  4. params, query
  5. localStorage/sessionStorage

讲一下 mixins 混入

mixins 定义了一部分可复用的方法或者计算属性。有点跟 Object.assign()

比如现在有两个组件,一个 Modal 模态框,一个 Toast 提示框,都有展示隐藏,切换布尔值控制。功能一样,看起来不一样,用法不一样,但是在逻辑上是一样的。这时候就可以用 mixins 混入。

  1. 选项合并
  2. 全局混入 Vue.mixin({})
// modal 模态框
const Model = {
  template: '#model',
  data() {
    return {
      isShowing: false
    };
  },
  method: {
    toggleShow() {
      this.isShowing = !this.isShowing;
    }
  },
  components: {
    appChild: Child
  }
};

// toast 提示框
const Toast = {
  template: '#toast',
  data() {
    return {
      isShowing: false
    };
  },
  method: {
    toggleShow() {
      this.isShowing = !this.isShowing;
    }
  },
  components: {
    appChild: Child
  }
};

// mixin.js
export const toggleShow = {
  data() {
    return {
      isShowing: false
    };
  },
  method: {
    toggleShow() {
      this.isShowing = !this.isShowing;
    }
  }
};

// model.vue
import { toggleShow } from '../mixin.js';

export default {
  mixins: [toggleShow],
  mounted() {}
};

讲一下 slot 插槽使用方式和场景

插槽相当于就是一个占位符。比如现在有 5 个页面,只有一个区域的内容不一样,其他内容都一样,此时就可以用插槽。

分匿名插槽、具名插槽、作用域插槽。

作用域插槽:为了解决在父组件在向子组件插槽传递模版内容时存在访问子组件数据的问题。

树状导航 点开个箭头再展示子菜单 怎么做

可以用插槽

<div class="nav-item" v-for>
  <span>nav name</span>
  <template v-slot:subNavItem></template>
</div>

// slot.vue
<slot name='subNavItem'>
  <div class='sub-nav-item' v-for></div>
</slot>

vue 文件里 在生命周期 create 中 箭头里面有个 this this 指向谁

根据 babel 的 es6 转 es5,是按严格模式来说的,this 一般会指向 undefined。

  1. css flex 布局 左 200px 中 300px 右 自适应 该怎么做
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      .wrap {
        display: flex;
      }
      .left {
        width: 200px;
        height: 200px;
        flex-shrink: 0;
        background-color: brown;
      }
      .center {
        width: 300px;
        height: 200px;
        flex-shrink: 0;
        background-color: blueviolet;
      }
      .right {
        /* width: 100%; */
        height: 200px;
        flex-basis: 100%; /*也可设置flex-basis*/
        background-color: bisque;
      }
    </style>
  </head>
  <body>
    <div class="wrap">
      <div class="left">11</div>
      <div class="center">22</div>
      <div class="right">333</div>
    </div>
  </body>
</html>

flex:1 是什么意思 有哪些参数

即 flex: 1 1 auto;

flex-grow: 设置盒子的扩展比率,默认为 0 flex-shrink: 设置盒子的缩小比例,默认为 1,缩小 flex-basis: 设置盒子收缩伸展的基准值,默认为 auto

用 vue 写百度搜索框

你会考虑到哪些? 点其他的地方 收起搜索的内容 这时候事件绑在哪里

  1. 防抖 debounce
  2. 点击阻止向上冒泡
<div class="search-box" click.stop="'showSearchResult"'>
  <div v-if="isShow" class="search-result-list"></div>
</div>

mounted() {
  document.addEventListener("click", () => {
    this.isShow = false;
  })
}

vue 反向代理 ???

其实这个面试官都问错了,这根本都不是 vue 提供的反向代理。是 vue-cli 脚手架用了 http-proxy-middleware 中间件。

可以在config/index.js dev 环境中中配置 proxyTable

dev: {
  ...
  proxyTable: {
    '/api': {
      target: 'https://www.example.com', // 跨域地址
      changeOrigin: true, // 是否跨域
      secure: true, // 是否使用https
      pathRewrite: {
        '^/api': '' // 匹配以/api开头的请求定制,并用''代替
      }
    }
  }
}

// 请求的地址相当于:https://www.example.com/getUserInfo
this.$http.post('/api/getUserInfo', {

}.then(res => {

}).catch(error => {

}))

AMD、CMD、CommonJS、ES6 Module

AMD

异步模块定义,采用异步方式加载模块,模块加载不影响后面语句的运行。

CMD

Common Module Definition, 通用模块定义。推崇依赖就近。

AMD 与 CMD 的区别

  1. AMD 推崇依赖前置,在定义模块的时候就要声明依赖的模块

  2. CMD 推崇就近依赖,只有在用到某个模块的时候再去 require

CommonJS

//
module.exports = {};

//
exports.xxx = xxx;

require('xxx');

特点:

  1. 所有代码都运行在模块作用域,不会污染全局作用域
  2. 模块是同步加载,即加载完成,才会执行后面的操作
  3. 模块在首次执行后就会缓存,再次加载只会返回缓存结果,若要再次执行,可清除缓存
  4. CommonJS 输出的值,是值的拷贝。即 require 导出的值是值的拷贝,模块内部发生变化也不会影响它

ES6 Module

//
export const xxx = xxx;

//
export default xxx;

import xxx from xxx;
import { xxx } from xxx;

CommonJS 与 ES6 Module 的区别

  1. CommonJS 是运行时加载,ES6 Module 是编译时输出接口

  2. CommonJS 是加载整个模块,将模块里的所有接口都加载进来;而 ES6 Module 是可以单独加载其中的某个接口

  3. CommonJS 是输出的是值的拷贝,ES6 Module 输出的是值的引用,被输出模块的内部的改变会影响引用的改变

  4. CommonJS 中的 this 指向当前模块,ES6 Module 中的 this 指向 undefined

  1. VO AO
  2. html 解析、渲染 link 会不会阻塞
  3. A

    解析过程
  4. vuex SPA 刷新的时候 数据会清空要怎么做 -> localStorage, sessionAStorage, cookie, indexDB,
  5. beforeDestroy 拿来做写什么 各个生命周期做些什么 -> 实例被销毁前,清除定时器,清除监听事件的操作。
  6. 闭包 做什么 有哪些场景用到 -> 模块化 实现 sum(30) // 30 sum(50) // 80 sum(1) // 81
function _sum(num) {
  var total = 0;
  return function() {
    total += num;
  };
}
let sum = _sum();
sum(30);
sum(50);
sum(1);
  1. webpack 配置哪些 说一下整个流程
  2. vue 静态路由
  3. vue dom diff 讲一下 算法大小是多少 还有其他的的吗
  4. prototype proto
  5. ts 泛型
  6. 脚手架生成
  7. 继承
  8. webpack splitChunks 中的 chunks 做什么的 -> chunks: 'initial', // initial(初始块)、async(按需加载块)、all(全部块),默认为 all;
  9. css 两列 左列宽度 200px 右边铺满 用不同的方式实现
  10. xss csrf
  11. webpack 抽取 css -> mini-css-extract-plugin
new MiniCssExtractPlugin({
  // Options similar to the same options in webpackOptions.output
  // both options are optional
  filename: `assets/css/[name]${watchMode ? '' : '.[chunkhash:4]'}.css`,
  chunkFilename: `assets/css/[id]${watchMode ? '' : '.[chunkhash:4]'}.css`,
  hot: true // optional as the plugin cannot automatically detect if you are using HOT, not for production use
}),
  1. 请用 ES5 和 ES6 实现类的继承:父类 Person 包含属性 name 和方法 sayName, 子类 Man 继承父类,并额外拥有 sex 和方法
  2. 有对象 json,请实现一个 treeLog 方法,以控制台缩进的方式打印这个对象的树状解构
const a = {b:1, c:1, d: {e: 'hh', f: 10}};

// 要求结果
--b:1
--c:1
--d
  --e:hh
  --f:10
  1. data(){} 为什么是个函数,而不是对象
  2. keep-alive 实现原理
  3. v-for key 是做什么的
  4. tcp 三次握手 四次挥手 https 浏览器输入 url 的整个过程
  5. MVVM 解释
  6. ts 参数都变成可选的 partical
  7. 301 302 304
Last Updated:
Contributors: kk