面试必刷!前端老赵吐血总结的前端面试题库(不断更新中)

前端老赵前端老赵 前端开发培训 943 0

面试必刷!前端老赵吐血总结的前端面试题库(不断更新中)

vue的双向绑定原理是什么?里面的关键点在哪里?

Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图。

通过Object.defineProperty()来劫持各个属性的setter, getter,在数据发生变动时通知Vue实例,触发相应的getter和setter回调函数。

其关键点是 Object.defineProperty() 方法,这个方法内有三个参数,分别为 obj(要定义其上属性的对象)、prop(要定义或修改的属性)、descriptor(具体的改变方法)简单来说,就是用这个方法定义一个值,当调用时我们使用了它里面的get方法;当我们给这个属性赋值时,同时又调用了里面的set方法



实现水平垂直居中的方式?

很多,但需要具体问题具体分析,常用的如:

  • flex 布局实现水平垂直居中,display: flex;justify-content: center;align-items: center;;

  • 绝对定位的方式;

  • 通过父元素使用 display: table 子元素使用 vertical-align: middle 实现水平垂直居中(水平居中采用 margin)


 

常用伪元素有哪一些?

  • ::before 该伪元素定义在元素之前添加内容;

  • ::after 该伪元素定义在元素之后添加内容;

  • ::first-line 该伪元素向文本的首行添加特殊样式;

  • ::first-letter 该伪元素向文本的第一个字母添加特殊样式;



移动端如何适配不同屏幕尺寸?


px 为主,搭配 vw/vh、媒体查询与 flex 进行布局(推荐):


  • 编写 <meta> 标签设置 viewport 的内容 width=device-width,让网页宽度等于视窗宽度;

  • 在 css 中使用 px;

  • 在适当的场景使用flex布局,或者配合vw进行自适应;

  • 在跨设备类型的时候(pc <-> 手机 <-> 平板)使用媒体查询;

  • 在跨设备类型如果交互差异太大的情况,考虑分开项目开发;

  • 关于布局,我们可以使用 flex 实现弹性布局,当实现特定宽高元素时,可以适当的使用 vw/vh,当特定的值使用 vw/vh 计算复杂或存在误差时,也可以使用 rem 。


flexible 方案:

使用rem,它是CSS3新增的一个相对单位;


vw/vh 布局:

vw/vh 方案与 rem 方案类似,都是将页面分成一份一份的,只不过 vw/vh 是将页面分为 100 份,1vw = device-width/100


 

本地存储有哪一些?他们三者有什么区别?


  • localStorage: 数据会永久存储,除非代码或手动删除(5M)

  • sessionStorage: 数据只存在于当前会话,浏览器关闭则清空(5M)

  • cookie: 设计初衷是用来和服务器通讯,而不是本地存储,他只是被‘借用’到本地存储。(4k),Cookie有一些缺点:存储空间小,最大4k、http请求时需要发送到服务器,增加请求数据量、只能用document.cookie=’…’ 来修改,太过简陋


 


JS的数据类型?如何判断js的数据类型?

基本数据类型(值类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol;

引用数据类型:对象(Object)、数组(Array)、函数(Function)。


 


JS数据类型判断:

  • typeof用以获取一个变量或者表达式的类型,一般用于判断值类型(基本类型)

  • instanceof 运算符是用来判断一个对象是否在其原型链原型构造函数的属性,所以在比较对象(引用类型)时才有意义

  • 通用完美的判断方法:Object.prototype.toString.call()


 


说一下ES6的新特性有哪些?


  • ES6 引入了class(类)

  • 模块化(Module),导出(export)、导入(import)

  • 箭头(Arrow)函数

  • 函数参数默认值

  • 模板字符串

  • 解构赋值



let、const、var三者有什么区别?


  • 主要是作用域(全局作用域、局部作用域),es6新增let、const

  • var声明的变量属于函数作用域,let和const声明的变量属于块级作用域;

  • var存在变量提升现象,而let和const没有;

  • var变量可以重复声明,而在同一块级作用域,let变量不能重新声明,const变量不能修改(基本类型),对于引用类型,仍然可以修该值。



数组去重有哪些办法?


利用reduce+includes (推荐)


reduce(function(total, currentValue, currentIndex, arr), initialValue)方法,可以接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

includes()方法用来判断一个数组是否包含一个指定的值,如果是返回true,否则false。


function unique(arr) {   
    return arr.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []);
}


 利用ES6 Set去重,但无法去掉{}空对象:

function unqiue(arr) {    return Array.from(new Set(arr))}


或者

var Arr = [...new Set(arr)]


利用for嵌套for,然后splice去重(ES5中最常用):


双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。


利用Map数据结构去重(推荐):


创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。


利用filter(推荐):


function unique(arr) { 
return arr.filter(function(item, index, arr) {   
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素   
return arr.indexOf(item) === index; 
});
}

 


说一下深拷贝和浅拷贝,如何自己实现一个深拷贝?


浅拷贝:如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝:不管原数据中值是什么类型的数据,拷贝后的新数据跟原数据是相互独立,没有关联的


实现深拷贝


1.利用json数据和json字符串之间的转换(缺点: 无法实现对 对象中 方法 的拷贝,会显示为undefined)

   JSON.parse(JSON.stringify(obj))

2.循环赋值和使用递归完整实现;


3.使用ES6新特性,解构赋值的方式(推荐)


    对象:let {...obj2} = obj

    

    数组: arr2 = arr.concat(); arr2 = arr.slice(0);

    

    let arr2 = [...arr]


 


Vue的生命周期有哪一些?说一下它们每个阶段做什么操作?


8个,vue2的生命周期


  • 创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象Data都为undefined,还未初始化。created阶段,vue实例的数据对象data有了,el还没有

  • 载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。

  • 更新前/后:当data变化时,会触发beforeUpdate和updated方法。

  • 销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。

  • activated: keep-alive组件激活时调用

  • deactivated: keep-alive组件销毁时调用

  • errorCaptured: 当捕获一个来自子孙组件的错误时被调用

  • 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。


具体说明:

  • beforeCreate() 创建前 在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在此生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法

  • created()被创建 data 和 methods都已经被初始化好了,可以调用了

  • beforeMount() 挂载前 在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的

  • mounted()已挂载 Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行

  • beforeupdate()更新前 页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步

  • updated()更新 页面显示的数据和data中的数据已经保持同步了,都是最新的

  • beforeDestroy() 销毁前 Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁

  • destroyed()被销毁 这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。


 

vue组件通讯方式有哪一些?


  • props/$emit(父传子,子传父,子调父)

  • $bus(任意组件传值,任意组件调方法)

        $bus.$emit();

        $bus.$on();

  • ref/$refs(父调子)

  • $parent/ $children(父子组件)

  • provide/reject(跨级组件)

  • Vuex(任意组件)

  • localStorage/sessionStorage(任意组件)

  • $attrs/$listeners(跨级组件)


 

Vuex有几个属性及作用?


  • state:用于数据的存储,是store中的唯一数据源

  • getters:从基本数据(state)派生的数据,相当于store的计算属性;getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

  • mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件;this.$store.commit()

  • actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作;this.$store.dispatch()

  • odules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护



Vuex的应用及 Vue内部的监听数据变化的机制


方法一、可以在组件中通过组件的 watch方法来做, 因为组件可以将state数据映射到 组件的计算属性上,然后 监听 映射的计算属性即可。


方法二、vuex中store对象本身提供了watch函数 ,可以利用该函数进行监听。


watch(fn: Function, callback: Function, options?: Object): Function


响应式地侦听 fn 的返回值,当值改变时调用回调函数。fn 接收 store 的 state 作为第一个参数,其 getter 作为第二个参数。最后接收一个可选的对象参数表示 Vue 的 vm.$watch 方法的参数。


created () {
    this.$store.watch((state, getters) => {
      return state.count
    }, () => {
      this.changeCount++
    })
  }

 


Vue的监听属性和计算属性有什么区别?


computed特性

  • 监测的是依赖值;

  • 计算属性不能执行异步任务,计算属性必须同步执行;

  • 具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不必再次执行函数


watch特性

  • 监测的是属性值;

  • 无缓存性,页面重新渲染时值不变化也会执行


 

说一下防抖和节流。怎么实现?


防抖:

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间;

实现方式:每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法;

缺点:如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟。


节流:

高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率;

实现方式:每次触发事件时,如果当前有等待执行的延时函数,则直接return 。


 


Vue的导航守卫有哪一些?


全局前置守卫:beforeEach(to, from, next),当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中;


  • 全局后置守卫:afterEach(to, from),它是在路由跳转完成后触发,它发生在 beforeEach 之后 beforeRouteEnter(组件内守卫) 之前, 因为是跳转完成后触发,所以没有 next 参数。

  • 全局解析守卫“beforeResolve”,在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用;

  • 路由独享的守卫“beforeEnter”;

  • 组件内的守卫

            “beforeRouteEnter”,在渲染该组件的对应路由被 confirm 前调用,不!能!获取组件实例 `this`,因为当守卫执行前,组件实例还没被创建;

            “beforeRouteLeave”导航离开该组件的对应路由时调用,可以访问组件实例 `this`;

            “beforeRouteUpdate”在当前路由改变,但是该组件被复用时调用,可以访问组件实例 `this`。


你的登录拦截怎么实现的?


路由拦截(路由守卫)

首先在定义路由的时候就需要多添加一个自定义字段 requireAuth,用于判断该路由的访问是否需要登录。如果用户已经登录,则顺利进入路由, 否则就进入登录页面。

定义完路由后,利用 vue-router 提供的钩子函数 beforeEach() 对路由进行判断。


http拦截器

路由拦截只是简单的前端路由控制,并不能真正阻止用户访问需要登录权限的路由。还有一种情况便是:当前token失效了,但是token依然保存在本地。这时候你去访问需要登录权限的路由时,实际上应该让用户重新登录。

这时候就需要结合 http 拦截器 + 后端接口返回的http 状态码来判断。

axios 的拦截器可通过配置http response inteceptor,当后端接口返回401 Unauthorized(未授权),让用户重新登录。


 

有用过图表吗?用的多吗?


  • ·  Echarts (广泛)

  • ·  antv-g2plot

  • ·  Chart.js

  • ·  Chartist

  • ·  FlexChart

  • ·  NVD3

  • ·  C3.js

  • ·  TauCharts

  • ·  ReCharts

  • ·  Flot


 

闭包是什么?如何实现?

  • 闭包是函数和声明该函数的词法环境的组合(就是能够读取其他函数内部变量的函数)。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

  • 创建闭包最常见方式,就是在一个函数内部创建另一个函数。

  • 闭包的this指向的是window

  • 最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

  • 注意:闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。



Vue2.0和vue3.0有什么区别?

生命周期

  • Vue3 在组合式API(Composition API,下面展开)中使用生命周期钩子时需要先引入,而 Vue2 在选项API(Options API)中可以直接调用生命周期钩子

  • Tips: setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地去定义。


多根节点

  • Vue3 支持多个根节点,也就是 fragment ,vue2不支持


Composition API

  • Vue2 是选项API(Options API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。

  • Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。


异步组件(Suspense)

Vue3 提供 Suspense 组件,允许程序在等待异步组件加载完成前渲染兜底的内容,如 loading ,使用户的体验更平滑。使用它,需在模板中声明,并包括两个命名插槽:default 和 fallback。Suspense 确保加载完异步内容时显示默认插槽,并将 fallback 插槽用作加载状态。


Teleport

Vue3 提供 Teleport 组件可将部分 DOM 移动到 Vue app 之外的位置。比如项目中常见的 Dialog 弹窗。


响应式原理

Vue2 响应式原理基础是 Object.defineProperty;Vue3 响应式原理基础是 Proxy。


虚拟DOM

Vue3 相比于 Vue2,虚拟DOM上增加 patchFlag 字段


事件缓存

Vue3 的cacheHandler可在第一次渲染后缓存我们的事件。相比于 Vue2 无需每次渲染都传递一个新函数。加一个 click 事件。


Diff算法优化

patchFlag 帮助 diff 时区分静态节点,以及不同类型的动态节点。一定程度地减少节点本身及其属性的比对。


 打包优化和typescript支持


Vue常用的指令有哪些?


  • v-model多用于表单元素实现双向数据绑定

  • v-bind 动态绑定

  • v-for 动态渲染,在写v-for的时候,都需要给元素加上一个key属性,key的主要作用就是来提高渲染性能的(能够更高效的更新虚拟 DOM)

  • v-show 显示内容,通过设置DOM元素的display样式,block为显示,none为隐藏

  • v-if 显示与隐藏,是通过控制dom节点的存在与否来控制元素的显隐

  • v-on 给标签绑定函数

  • v-text 用于解析文本,但不能解析标签,并且会覆盖元素内部原有的内容!

  • v-html可以把带有标签的字符串,渲染成真正的 HTML 内容!


v-If和v-show有什么区别?

v-if 是动态添加,当值为 false 时,是完全移除该元素,即 dom 树中不存在该元素。

v-show 仅是隐藏 / 显示,值为 false 时,该元素依旧存在于 dom 树中。若其原有样式设置了 display: none 则会导致其无法正常显示。


v-for为什么要加一个key?


  • 设置key值是为了提高动态加载的效率。

  • 因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM。

  • key主要用来做dom diff算法用的,diff算法是同级比较,比较当前标签上的key还有它当前的标签名,如果key和标签名都一样时只是做了一个移动的操作,不会重新创建元素和删除元素。

  • 有增删操作的需要用唯一标识(如:id)作为key值


你是如何封装一个组件的?

vue将组件分为全局组件和局部组件;可以分为函数式(无状态)组件和普通(无状态)组件

 

开放式问题:

  • 有自己从0到1搭建过项目吗?

  • 有用过uni-app吗?

  • 你会写后台吗?有搞过服务端渲染吗?

  • 说一下你项目中遇到的难点,如何解决?



Url到浏览器的一个过程有哪些步骤?

  1. 浏览器查看缓存,如果请求资源在缓存中失效或者资源未缓存,则发起新请求,跳转到转码步骤

  2. DNS解析——解析域名,获取对应的ip地址

  3. TCP连接——TCP三次握手

  4. 浏览器发送http请求

  5. 服务器处理请求并返回http报文

  6. 浏览器解析返回的数据并渲染页面

  7. 断开连接:TCP四次挥手


如何实现小程序的request封装及拦截?

TBD


在vue的项目应用中,不使用框架,怎么封装?


 TBD


什么是Js原型?原型链是什么?


在js中,每个构造函数内部都有一个prototype属性,该属性的值是个对象,该对象包含了该构造函数所有实例共享的属性和方法。当我们通过构造函数创建对象的时候,在这个对象中有一个指针,这个指针指向构造函数的prototype的值,我们将这个指向prototype的指针称为原型。

或者用另一种简单却难理解的说法是:js中的对象都有一个特殊的[[Prototype]]内置属性,其实这就是原型

原型链就是通过原型组成的一条查找链。


 


用闭包的原理做过哪些封装或应用?

最常用的:

  • 防抖函数和节流函数;

  • getter和setter的封装函数(vue响应式即是通过闭包实现)

  • Vue中data返回值,computed返回值都是闭包的应用



作用域是什么?

作用域是在运行时代码中的某些特定部分的变量,函数和对象的可访问性。(作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。)

作用域与执行上下文:

 JavaScript 属于解释型语言,JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:

解释阶段:词法分析、语法分析、作用域规则确定

执行阶段:创建执行上下文、执行函数代码、垃圾回收

JavaScript 解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是 this 的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。

作用域和执行上下文之间最大的区别是:

执行上下文在运行时确定,随时可能改变;

作用域在定义时就确定,并且不会改变。

一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。


操作数组的方式有哪些?

  • isArrary():判断参数是不是数组,返回布尔值Array.isArrary();

  • Array.from():对伪数组或可迭代对象(包括arguments Array,Map,Set,String…)转换成数组对象

  • toString():把数组转成字符串------数组名.toString–对象类型数组不能直接转换;

  • valueOf():返回数组本身-----数组名.valueOf();

  • push():在数组末尾添加元素;

  • unshift():在数组开头添加元素;

  • pop():删除数组末尾的值,并返回删除的值;

  • shift():删除数组第一个的值,并返回删除的值;

  • reverse(): 翻转数组;

  • join(‘分割符号’):以所用的符号对数组进行拼接方法;

  • sort():排序;

  • concat(数组名1,…数组名N):把多个数组拼接成一个数组 并且不影响原有的数组;

  • slice(开始索引,结束索引):根据索引截取里面参数(下标),包括左边不包括右边;如果第二个数是正数,返回从开始位置到结束的位置(但不包括最后结束的位置);如果第二个数是负数,返回的是开始位置到结束位置减一;如果第二个数是0或者两个数都是负数,则返回[]。

  • splice(index, howmany, item1, ....., itemX):向/从数组添加/删除项目,并返回删除的项目,会改变原始数组.

  • index必需:整数,指定在什么位置添加/删除项目,使用负值指定从数组末尾开始的位置。howmany        可选。要删除的项目数。如果设置为 0,则不会删除任何项目。item1, ..., itemX        可选。要添加到数组中的新项目。

  • indexOf():某个元素从左到右第一次出现的下标;

  • lastIndexOf:某个元素从左到右最后一次出现的下标;

  • filter():过滤满足条件为true的元素,返回一个新数组,不会影响原数组,筛选单个值;

  • find():查找数组中满足条件的第一个元素,返回该元素,没找到输出undefinded ;

  • findIndex():查找数组中满足条件的第一个元素,返回该元素下标,没找到输出-1 ;

  • includes():判断数组是否包含某个元素,不用回调函数(不用函数作为参数)不包含返回false ;

  • map(function(v,index,arr){ }):返回原来的数组操作之后的数据,返回新数组不改变原数组;

  • forEach():方法对数组的每个元素执行一次提供的函数;

  • every():全部满足条件 全部都满足则返回true,否则返回false 相当于&& ;

  • some():只要有一个满足就返回true 全部都不满足则返回false 相当于|| ;

  • reduce(function(返回值,元素,下标,当前数组){},返回值的初始值):reduce()方法接收一个函数累加器,数组中的每个值(从左到右)开始缩减,最后累计为一个值。


 


0.1 + 0.2 等于 0.3吗?为什么?如何解决?

不等于(两个值转化为二进制是循环小数,计算过程会精度丢失),


解决:

一、先将其转化为整数,运算之后,然后再将其转化为小数;

二、用Math.js等数学计算库来解决。



keep-alive是什么?有哪几个生命周期阶段?

<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。作用:缓存组件内部状态,避免重新渲染。

当引入keep-alive的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。


用法:

keep-alive可以接收3个属性做为参数进行匹配对应的组件进行缓存:

include包含的组件(可以为字符串,数组,以及正则表达式,只有匹配的组件会被缓存)

exclude排除的组件(以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)

max缓存组件的最大值(类型为字符或者数字,可以控制缓存组件的个数)


 


判断一个变量是否是数组,有哪些办法?


  • 调用Object.prototype.toString.call(),返回true,则说明该变量是数组类型;反之,说明该变量不是数组类型。console.log(Object.prototype.toString.call(arr) === '[object Array]')

  • 用 instanceof 运算符, 该运算符左边是我们想要判断的变量, 右边则是我们想要判断的对象的类型

console.log(typeof arr)
  • 利用构造函数来判断他的原型是否为Array:

console.log(arr.constructor === Array)


  • 利用的一个专门的方法 isArray(), 用法:Array.isArray(变量),返回true,则说明该变量是数组类型;反之,说明该变量不是数组类型

console.log(Array.isArray(arr))


  • 通过对象的原型方式来判断

console.log(arr.__proto__ === Array.prototype)


  • 通过 Object.getPrototypeOf()来判断是否为数组类型

function unqiue(arr) {    return Array.from(new Set(arr))}0


  • 通过 isPrototypeOf() 方法来判断是否为数组类型

function unqiue(arr) {    return Array.from(new Set(arr))}1


判断一个变量是否是对象,有哪些办法?

  • 判断字符串得到string,数字和NaN得到number,函数会得到function等,但是判断数组,对象和null时都会得到object,这就是typeof的局限性,并不能准确的判断该变量的"真实身份"。

  • instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,意思就是该变量通过原型链上能否找到构造函数的prototype 属性

function unqiue(arr) {    return Array.from(new Set(arr))}2
  • Object.prototype.toString.call()方法可以精准判断变量类型,它返回[object constructorName]的字符串格式


  • constructor:


console.log(arr.constructor === Array); //true
console.log(arr.constructor === Object); //false
console.log(obj.constructor === Object); //true



对象常用方法有哪些?


  • for in 遍历对象的属性

  • Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致;

  • Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。将现有对象作新对象的 proto。

  • Object.assign() 方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。返回目标对象。

  • Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)。Object.assign(target, …sources)

  • Object.hasOwnProperty() 返回一个布尔值,用来检测对象自身属性中是否具有指定的属性。和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。

  • Object.isPrototypeOf()用于测试一个对象是否存在于另一个对象的原型链上。

  • a.isPrototypeOf(b);判断的是a对象是否存在于b对象的原型链之中

  • A instanceof B 判断的是B.prototype是否存在与A的原型链之中

  • Object.defineProperty() 劫持变量的set和get方法,将属性添加到对象,或修改现有属性的特性


 

创建一个空数组/空对象有哪些方式?


  • let emptyObj1 = {};

  • let emptyObj2 = new Object();

  • let emptyObj2 = Object.create(Object.prototype);

  • let arr1 = new Array()

  • let arr2 = []


 

Set和Map各是什么?


  • Map是键值对,Set是值的集合,当然键和值可以是任何的值;

  • Map可以通过get方法获取值,而set不能因为它只有值;

  • 都能通过迭代器进行for...of遍历;

  • Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储

  • map和set都是stl中的关联容器,map以键值对的形式存储,key=value组成pair,是一组映射关系。set只有值,可以认为只有一个数据,并且set中元素不可以重复且自动排序。

  • Map对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map可以接受一个数组作为参数。

  • Map对象的属性:

    • size:返回Map对象中所包含的键值对个数

  • Map对象的方法:

    • set(key, val): 向Map中添加新元素

    • get(key): 通过键值查找特定的数值并返回

    • has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false

    • delete(key): 通过键值从Map中移除对应的数据

    • clear(): 将这个Map中的所有元素删除

  • Set对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。

  • Set 本身是一个构造函数,用来生成Set 数据结构。Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

  • Set实例对象的属性

    • size:返回Set实例的成员总数

  • Set实例对象的方法

    • add(value):添加某个值,返回 Set 结构本身(可以链式调用)。

    • delete(value):删除某个值,删除成功返回true,否则返回false。

    • has(value):返回一个布尔值,表示该值是否为Set的成员。

    • clear():清除所有成员,没有返回值。




介绍一下promise。

Promise是JS中进行异步编程的新的解决方案;指定回调函数的方法更加灵活;支持链式调用 ,可以解决回调函数问题。

Promise通常会解决三种问题

  • (1)链式回调

  • (2)同时发起几个异步请求,谁先有结果就拿谁的

  • (3)发起多个请求,等到所有请求后再做下一步处理



Promise.all 方法:

说明:只有所有的 promise 都成功才成功, 只要有一个失败了就直接失败;返回一个新的 promise(成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值)。

`Promise.all(promises) => {} ` promises: 包含 n 个 promise 的数组

Promise.race 方法:

说明:第一个完成的 promise 就是最终的结果状态;返回一个新的 promise。


`Promise.race(promises) => {} ` promises: 包含 n 个 promise 的数组


Promise.allSettled 方法:


可用于并行执行独立的异步操作,并收集这些异步操作的结果。

函数接受一个 promise 数组(或通常是一个可迭代的)作为参数,当所有输入 promises 都被履行或拒绝时,statusesPromise 会解析为一个具有其状态的数组。 返回的承诺总是以一系列状态实现,无论是否有一些(或者全部)输入承诺被拒绝。


Promise.allSettled() 和 Promise.all() 的最大不同:Promise.allSettled() 永远不会被 rejected 。


 


如何改变一个函数a的上下文?

call()、apply()、bind()


Call和applay有什么区别?

没有本质区别,参数形式不同



Evenbus是什么东西?


父子组件生命周期执行顺序是怎么样的?


  • 创建与挂载

父beforeCreate > 父created > 父beforeMount > 子beforeCreate > 子created > 子beforeMount > 子mounted > 父mounted

  • 更新

父beforeUpdate > 子beforeUpdate > 子updated > 父updated

  • 销毁

父beforeDestroy > 子beforeDestroy > 子destroyed > 父destroyed


mixins有几个生命周期阶段?

mixin的beforeCreate > 父beforeCreate > mixin的created > 父created > mixin的beforeMount > 父beforeMount > 子beforeCreate > 子created > 子beforeMount > 子mounted > mixin的mounted >父mounted


弹性布局,一行两列,一列固定宽,如何实现?

对父元素设置弹性flex,使两个区域分成两列。给固定侧边栏#aside的宽高赋具体值。#detail只给高度值,然后使复合样式flex:1;

flex:1;为flex-grow=1,flex-shrink=1,flex-basis=:0;的复合写法


 

Flex:1 包含哪三种属性(flex: 1 1 0%;)

第一个参数表示: flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大

第二个参数表示: flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小

第三个参数表示: flex-basis给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小



首屏加载优化方案

  • 使用CDN资源,减小服务器带宽压力

  • 路由懒加载

  • 将一些静态js css放到其他地方(如OSS),减小服务器压力

  • 按需加载三方资源,如iview,建议按需引入iview中的组件

  • 使用nginx开启gzip减小网络传输的流量大小

  • 若首屏为登录页,可以做成多入口,登录页单独分离为一个入口

  • 使用uglifyjs-webpack-plugin插件代替webpack自带UglifyJsPlugin插件

  • 异步组件,类似路由懒加载

  • js外联文件放到body底部,css外联文件放到head内

  • http静态资源尽量用多个子域名

  • 尽量减少http requests的数量

  • js/css/html/img资源压缩

  • 使用css spirtes,可以减少img请求次数

  • 大图使用lazyload懒加载

  • 避免404,减少外联js

  • 减少cookie大小可以提高获得响应的时间

  • 减少dom elements的数量

  • 使用异步脚本,动态创建脚本



js 类型的显示转换和隐式转换


  • 显示转换:最简单的方式是使用函数:

  • 转换为字符串:toString() 或 String()

  • 转换为数值:Number()、parseInt()、parseFloat()

  • 转换为布尔值:Boolean()

  • 转换为对象:Object()

  • 隐式转换:当运算符在运算时,两边数据不统一,编译器会自动将两边数据进行数据类型转换成统一的再计算。

  • 触发隐式转换的条件:一元、二元(+)、==、if、? :、&&

  • 逻辑语句的类型转换:当使用if、while、for 时,隐式转换为布尔值;

  • 逻辑表达式:! 逻辑非,隐式转换为布尔值,并取反,!!两次逻辑非,隐式转换为布尔值;|| 和 && 会将非布尔值操作数,隐式转换为布尔值,再判断;

  • 算术表达式:具体分析


常用:


  • x + "" // 等价于 String(x)

  • +x // 等价于 Number(x),也可以写成 x - 0

  • !!x // 等价于Boolean(x)

  • !x // 转换为布尔值,并取反


 

vue如何实现权限管理


权限管理

权限管理就是让不同的用户只能访问自己权限内的资源,有以下几种

  • 路由权限,用户登录后只能看到自己权限内的导航菜单,且只能访问自己权限内的路由地址

  • 视图权限,用户只能看到自己权限内的内容和按钮

  • 请求权限,越权请求将其拦截


控制权限

接口权限:用户登录成功后可以得到一个token,将token存起来,通过axios请求拦截器进行拦截,请求头里要携带token

按钮权限:

  • 方法一、用v-if判断,当如果页面很多的时候,每个页面都要获取用户权限role和路由表里的meta.btnUse,然后再做判断,比较繁琐

  • 方法二、通过自定义指令进行按钮权限的判断。


菜单权限:菜单权限可以理解成将页面与路由进行解耦。

  • 方法一、前端定义路由信息;全局路由守卫里做判断。根据路由name找不到对应的菜单,就表示用户有没权限访问,如果路由很多,可以应用初始化时,只挂载不需要权限控制的路由。拿到后端返回的菜单后,根据菜单与路由的对应关系,筛选出可访问的路由,通过addRoutes动态挂载。

  • 方法一缺点:菜单需要和路由一一对应,前端添加了新功能,需要通过菜单管理功能添加新的菜单,如果菜单配置的不对会导致应用不能正常使用全局路由守卫里,每次路由跳转都要做判断

  • 方法二、菜单和路由都由后端返回,前端统一定义路由组件,后端返回路由,将后端返回的路由通过addRoutes动态挂载,需要处理数据,将component字段换成对应的组件,注意嵌套路由的数据遍历。

  • 方法二缺点:全局路由守卫里,每次路由跳转都要做判断;需要前后端配合。


路由权限:

  • 方法一、在路由初始化的时候挂载全部路由,在路由上标记相应的权限信息,当路由跳转的时候做校验。

  • 方法一缺点:会加载所有的路由,当路由很多的时候,对性能会有影响;每次路由跳转都要做权限判断;菜单信息写在前端,需要修改标题的时候,需要重新编译;菜单跟路由耦合在一起,路由不一定作为菜单显示,还要多加字段进行标识。

  • 方法二、初始化的时候先挂载不需要权限控制的路由,例如登录页。如果用户通过URL访问,则会跳转到404页面,登录后,获取用户的权限信息,然后筛选有权限访问的路由,在全局路由守卫里进行调用addRoutes添加路由。

  • 方法二缺点:全局路由守卫里,每次路由跳转都要做判断;菜单信息在前端,要修改个标题,需要重新编译;菜单跟路由耦合在一起,路由不一定作为菜单显示,还要多加字段进行标识




CSS盒子模型

W3C盒子模型(标准盒模型)

1.标准盒模型中width指的是内容区域content的宽度;height指的是内容区域content的高度。

2.标准盒模型下盒子的大小 = content + border + padding + margin


IE盒子模型(怪异盒模型)

1.怪异盒模型中的width指的是内容、边框、内边距总的宽度(content + border + padding);height指的是内容、边框、内边距总的高度

2.怪异盒模型下盒子的大小=width(content + border + padding) + margin


设置盒子模型(CSS3指定盒子模型种类)

  • box-sizing: content-box;

  • box-sizing: border-box;

  • box-sizing: inherit;

即box-sizing属性可以指定盒子模型种类,content-box指定盒子模型为W3C(标准盒模型),border-box为IE盒子模型(怪异盒模型)。



本文系前端老赵独家发表,未经许可,不得转载。

喜欢0发布评论

评论列表

  • 数码大师 发表于 2年前

    涵盖了很多方面的知识,让我对这个领域的认识更加深入,感谢老师的精彩分享!

发表评论

  • 昵称(必填)
  • 邮箱
  • 网址