前端面试题

登录怎么写

用户输入用户名密码 之后向后台发送请求,之后会返回token,把返回的token存入到session里面去 之后使用导航守卫 router.beforeEach 在路由请求前去本地取出寸的session 如果没有 说明没有登陆

router.beforeEach((to, from, next) => {
  /*  to  将要访问的路径  form 表示从哪个路径跳转而来 next 是一个函数表示放行 */ 
  if (to.path === '/login') return next()
  const tokenStr = window.sessionStorage.getItem('token')
  if (!tokenStr) return next('/login')
  next()
})

然后在axios 拦截器里面把token放到请求头里面

//例子
axios.interceptors.request.use(config => {
  // 为请求头对象,添加token验证的Authorization字段
  config.headers.Authorization = window.sessionStorage.getItem('token')
  return config
})

1.箭头函数和普通函数的区别

  1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
  2. 箭头函数不能绑定arguments
  3. 箭头函数没有原型属性
var a = ()=>{
  return 1;
}

function b(){
  return 2;
}

console.log(a.prototype);  // undefined
console.log(b.prototype);   // {constructor: ƒ}
  1. 箭头函数的this永远指向其上下文的this,没有办改变其指向,普通函数的this指向调用它的对象
var obj = {
  a: 10,
  b: () => {
    console.log(this.a); // undefined 箭头函数this指向window
    console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
  },
  c: function() {
    console.log(this.a); // 10
    console.log(this); // {a: 10, b: ƒ, c: ƒ}
  }
}
obj.b(); 
obj.c();

2. foreach怎么打断

  1. forEach本身是没有打断操作的
  2. 可以使用抛出异常的方式跳出循环
var a=[1,2,3,4,5]
try{
a.forEach(function(item,i){
    console.log(item)
    if(item===2){
    throw new Error("跳出来")
    }
})
}catch(e){
    
}
//输出 1 2  undefined 

3. js的原型和原型链

简单来答就是
函数Person(对象)有个属性prototype(指针)指向原型对象Person.prototype(原型对象,实质也是对象),他有个属性constructor(指针) ,又指向 Person函数对象(互指)

再理解就是
每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。

father 就是一个构造函数,每个函数都有一个 prototype 属性,
我们给 father 的 prototype上添加一个 name属性 为 ’ Tom ’
我们使用 new 创建了一个实例对象 son
son 便继承了 father 的 name

function father() {
}
var son= new father();

father.prototype.name = 'Tom';

console.log(son.name) // Tom

每一个JavaScript对象(除了 null )都具有的一个属性,叫__proto__,这个属性会指向该对象的原型

function father() {
}
var son= new father();
console.log(son.__proto__ === Person.prototype); // true

每个原型都有一个 constructor 属性指向关联的构造函数 实例原型指向构造函数

function father() {
}
console.log(father === father.prototype.constructor); // true

当如果继承了父亲的name属性之后 自己又有个新的name属性的时候

function father() {

}

father.prototype.name = 'Kevin';

var son= new father();

son.name = 'Daisy';
console.log(son.name) // Daisy

delete son.name;
console.log(son.name) // Kevin

当我们删除了 son的 name 属性时,读取 son.name,从 son对象中找不到 name 属性就会从 son的原型也就是 son.proto ,也就是 father.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。

4. js实现无限层级树形数据结构

用递归 根据数据结构使用 dom拼接

5. VUE 父子通信 兄弟通信

  • 父子通信

父组件与子组件通信 父组件在data里面定义好数据 子组件通过props来接受父组件的数据

子组件与父组件通信 .$emit

兄弟之间用 vuex

6. ES6有哪些新特性

  1. 变量声明:由var变为let和const;
  2. 模板字符串:使用反引号``;在模板字符串里面支持换行,并可以在里面使用${}来包裹一个变量或表达式;
  3. 解构:有数组解构和对象解构;可以快速获取数组和对象的值;
//数组结构
const foodList=['西兰花','花菜','韭菜']
const [food1,food2,food3] = foodList
 // food1  西兰花 
 // food2  花菜
 // food3 韭菜 
 //对象结构
 const person={
    name:"小明",
    sex:"男",
}
const {name,sex} = person
  • 展开运算符:在ES6中用…来表示展开运算符,它可以将数组或者对象进行展开;
  • 箭头函数

7 . Vue路由传参query和params的区别

query使用path和name传参跳转都可以,而params只能使用name传参跳转

传参跳转页面时,query和params不需要在路由上配参数就能在新的页面获取到参数,
但是params不在路由配参数的话,当用户刷新当前页面的时候,参数就会消失。
也就是说使用params不在路由配置参数,只有第一次进入页面参数有效,刷新页面参数就会消失。

query传参

//在方法里面写 也可以用name
this.$router.push({ path:'/search', query: { categoryId: this.categoryId }})

// router-link里面的to
<router-link :to="{path:'/search',query: {categoryId: item.categoryId}}">

接收参数:this.categoryId = this.$route.query.categoryId ;
params传参

//在方法里面写
this.$router.push({ name::'/search', query: { categoryId: this.categoryId }})

// router-link里面的to
<router-link :to="{name::'/search',query: {categoryId: item.categoryId}}">

接收参数:this.categoryId = this.$route.params.categoryId ;

8.远程 j s 数据绑定原理

通过 Object.defineProperty() 来劫持各个属性的 setter / getter,在数据变动时发布消息给订阅者,触发相应的监听回调

9. VUE 虚拟 DOM

  • 文档对象模型或DOM定义了一个接口,该接口允许JavaScript之类的语言访问和操作HTML文档。元素由树中的节点
    表示,并且接口允许我们操纵它们。但是此接口需要付出代价,大量非常频繁的DOM操作会使页面速度变慢

  • 每个元素都是一个节点,每段文字也是一个节点,一个节点就是页面的额一部分,就像家谱一样,每个节点都有孩子节点。

  • 虚拟dom就是为了解决操作真是dom带来的性能问题而出现的,所以说,

  • 虚拟dom就是用js对象模拟真实的dom节点,也就是将所有的更新dom的操作先全部反映在js对象上(虚拟dom上)

  • 操作内存中的js对象显然速度要快很多,等更新完成后,再将最终的js对象映射到真是的dom上,交由浏览器去绘制页面。

webpack之性能优化

1. 模块化引入 按需导出
2. 优化resolve.extensions配置 设置解析文件后缀
resolve: {
    extensions: ['.js', '.json', 'jsx']
}
3.优化resolve.modules配置
  • resolve.modules 用于配置Webpack去哪些目录下寻找第三方模块。
    • resolve.modules的默认值是[node modules],
    • 含义是先去当前目录的/node modules目录下去找我们想找的模块,
    • 如果没找到,就去上一级目录…/node modules中找,再没有就去
    • 当安装的第三方模块都放在项目根目录的./node modules目录下时,
    • 就没有必要按照默认的方式去一层层地寻找,
    • 可以指明存放第三方模块的绝对路径,以减少寻找
resolve: {
    // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
    modules: [path.resolve(__dirname,'node_modules')]
}
4. 优化loader配置 缩小文件搜索范围

由于Loader对文件的转换操作很耗时,所以需要让尽可能少的文件被Loader处理。我们可以通过以下3方面优化Loader配置:
(1)优化正则匹配
(2)通过cacheDirectory选项开启缓存
(3)通过include、exclude来减少被处理的文件。

计算属性和 watch 的区别

回答该题前,一般都会思考一下。很多人会偏题,直接去答计算属性和 watch 怎么用,这是不得分的,因为题目是问区别,并不是用法。

计算属性是自动监听依赖值的变化,从而动态返回内容,监听是一个过程,在监听的值变化时,可以触发一个回调,并做一些事情。

所以区别来源于用法,只是需要动态值,那就用计算属性;需要知道值的改变后执行业务逻辑,才用 watch,用反或混用虽然可行,但都是不正确的用法。

这个问题会延伸出几个问题:

  1. computed 是一个对象时,它有哪些选项?
  2. computed 和 methods 有什么区别?
  3. computed 是否能依赖其它组件的数据?
  4. watch 是一个对象时,它有哪些选项?

问题 1,有 get 和 set 两个选项。

问题 2,methods 是一个方法,它可以接受参数,而 computed 不能;computed 是可以缓存的,methods 不会;一般在 v-for 里,需要根据当前项动态绑定值时,只能用 methods 而不能用 computed,因为 computed 不能传参。

问题 3,computed 可以依赖其它 computed,甚至是其它组件的 data

问题 4,有以下常用的配置:

  • handler 执行的函数
  • deep 是否深度
  • immediate 是否立即执行

事件修饰符

这个问题我会先写一段代码:

内容

然后问:怎样给这个自定义组件 custom-component 绑定一个原生的 click 事件?

我一开始并不会问什么是事件修饰符,但是如果候选人说 ,就已经错了,说明它对这个没有概念。这里的 @click 是自定义事件 click,并不是原生事件 click。绑定原生的 click 是这样的:

内容

该问题会引申很多,比如常见的事件修饰符有哪些?如果你能说上 .exact,说明你是个很爱探索的人,会大大加分哦。

.exact 是 Vue.js 2.5.0 新加的,它允许你控制由精确的系统修饰符组合触发的事件,比如:










你可能还需要了解常用的几个事件修饰符:

  • .stop
  • .prevent
  • .capture
  • .self

而且,事件修饰符在连用时,是有先后顺序的。

组件中 data 为什么是函数

为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?

因为组件是用来复用的,JS 里对象是引用关系,这样作用域没有隔离,而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。

keep-alive 的理解

这是个概念题,主要考察候选人是否知道这个用法。简单说,就是把一个组件的编译缓存起来。

递归组件的要求

回答这道题,首先你得知道什么是递归组件。而不到 10% 的人知道递归组件。其实在实际业务中用的确实不多,在独立组件中会经常使用,第 14 节和 15 节专门讲过递归组件。那回到问题,递归组件的要求是什么?主要有两个:

  • 要给组件设置 name
  • 要有一个明确的结束条件。

Vuex 中 mutations 和 actions 的区别

主要的区别是,actions 可以执行异步。actions 是调用 mutations,而 mutations 来修改 store。

1、vuex有哪几种属性?

答:有五种,分别是 State、 Getter、Mutation 、Action、 Module

2、vuex的State特性是?

一、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于与一般Vue对象里面的data
二、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
三、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中

3、vuex的Getter特性是?

一、getters 可以对State进行计算操作,它就是Store的计算属性
二、 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
三、 如果一个状态只在一个组件内使用,是可以不用getters

4、vuex的Mutation特性是?

一、Action 类似于 mutation,不同在于:
二、Action 提交的是 mutation,而不是直接变更状态。
三、Action 可以包含任意异步操作

5、Vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?

一、如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
二、如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用,并包装成promise返回,在调用处用async await处理返回的数据。如果不要复用这个请求,那么直接写在vue文件里很方便。

6、不用Vuex会带来什么问题?

一、可维护性会下降,你要想修改数据,你得维护三个地方

二、可读性会下降,因为一个组件里的数据,你根本就看不出来是从哪来的

三、增加耦合,大量的上传派发,会让耦合性大大的增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。

你可能感兴趣的:(日常学习)