自己搜集的一些前端面试题 初中级(较多)

Map 和Set的区别?

1.

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

    2.

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

3.

都能通过迭代进行for of 遍历

4.

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

Map 和Object的区别?

Object本质是哈希结构的键值对集合 它只能用字符串 数组 或者Symbol等简单数据类型当作键,这就带来很大的限制

Map可以被for of 迭代  Object不行   Map有序 Object无序



 

// 14 10  5 6  7  2 3 8 9

 无法去掉{ } 对象

function unique(arr) {

    return Array.form(new Set(arr))

}

// es6  数组去重

// es5 数组去重

function unique(arr) {

    for (var i = 0; i < arr.length; i++) {

        for (var j = i + 1; j < arr.length; j++) {

            if (arr[i] == arr[j]) {

                arr.splice(j, 1);

                j--

            }

        }

    }

}


 

forEach()

// 方法针对每一个元素执行提供的函数, 因该方法没有返回值, 是否改变原数组取决于与数组元素的类型是基本类型还是引用数据类型

map()

    // 方法不会改变原数组的值, 返回一个新数组, 新数组的值为原数组调用函数处理之后的值

    // New 操作符做了什么?

    ```

1.首先创建了一个新对象

2.设置原型,将对象的原型设置为函数的prototype对象,

3.让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)

4.判断函数的返回值类型,如果值类型,返回创建的对象.如果是引用类型,就返回这个引用类型的对象

```

TCP三次握手

1.第一次握手:建立链接时,客户端发送syn包到服务器,并进入请求连接状态,等待服务器确认

2.第二次握手:服务器收到syn包并确认客户的syn包.同时发送syn+ack包

3.第三次握手:客户端收到服务器的syn+ack包,向服务器发送ack包 发送完毕客户端和服务器端进入链接成功状态 完成三次握手


 

TCP四次挥手

1.客户端发送fin 用来关闭客户到服务端的数据传送

2.服务端收到fin  发送ack 确认序号为收到序号+1

3.服务端关闭与客户端的链接,发送一个fin给客户端

4.客户端发回ACK报文确认,并将确认序号设置为收到序号加1

为什么三次握手?

为了防止已经失效的连接请求报文突然又传到服务端,因而产生错误

1.避免重复链接

2.防止旧的重复链接引起连接混乱的问题

// 全局事件总线

Vue.prototype.$bus = new Vue()   //自定义事件

// 1.Http和https的基本概念

1.http是一个客户端和服务器端请求和应答的标准(TCP), 用于从www服务器传出超文本到本地浏览器的超文本传输协议

2.https是以安全为目标的HTTP通道, 即, HTTP下加入SSL层加密 建立一个信息安全通道, 确保数据的传输, 保证网站真实性

// 1.http是超文本传输,信息是明文传出.https更安全 经过ssl加密

// 2.http 默认端口为80 https默认端口为443,

// 3.http连接简单 无状态,https握手比较费时

怎么理解 http是无状态的

服务器无法给不同的客户端响应不同的信息

:因为当浏览器第一次发送数据给服务器时,服务器响应了;如果同一浏览器,向服务器第二次发送请求时,它还是会响应,

但服务器并不知道你就是刚才那个浏览器。简而言之,服务器是不会记住你是谁的,

所以是无状态的。

如果要使http协议有状态,就可以使浏览器访问服务器时,加入cookie


 

2.1、HTTPS协议需要CA证书, 费用较高; 而HTTP协议不需要

2、HTTP协议是超文本传输协议, 信息是明文传输的, HTTPS则是具有安全性的SSL加密传输协议;

3、使用不同的连接方式, 端口也不同, HTTP协议端口是80, HTTPS协议端口是443;

4、HTTP协议连接很简单, 是无状态的; HTTPS协议是具有SSL和HTTP协议构建的可进行加密传输、身份认证的网络协议, 比HTTP更加安全

5.https协议需要ca证书  费用高

什么是模块化?

本质:一个实现特定功能的js文件

开发:是一个管理方法,一种生产方式,实现什么功能就加载什么模块

特点:

1.避免了变量名冲突

2.减少了全局变量

3.高效复用,便于维护


 

跨域是指: 对浏览器不能执行其他网站的脚本.它是由浏览器的同源策略造成的,

同源策略: 是浏览器对JS实施的安全限制, 只要协议.域名.端口号.有任何一个不同就被当做不同的域,

跨域原理: 即是通过各种方式避开浏览器的安全限制.

本地环境

1、用webpack代理

2、开启跨域浏览器

3、本地代理工具代理

4、后端更改cros orgin配置

线上环境

1、服务端nginx代理

2、后端更改配置

nginx  proxy  

设计图需要用蓝狐


 

!Cookie、sessionStorage、localStorage 的区别

相同点: 存储在客户端

不同点:

cookie数据大小不能超过4k, sessionStorage和localStorage的存储比cookie大得多.可以达到5M +,

 cookie设置过期事件之前一直有效, localStorage永久存储, 浏览器关闭后数据不丢失除非主动删除.sessionStorage是在浏览器关闭后清空

cookie的数据会自动传递到服务器: sessionStorage和localStorage数据会保存在本地



 

浏览器的重绘与重排的区别

重排 / 回流(reflow): 当DOM变化影响了元素的几何信息, 浏览器需要重新计算元素的集合属性, 将其安放在界面的正确位置, 这个过程叫重排, 表现为重新生成布局.重新排列元素.d

重绘当一个元素的外观发生改变,但没有改变布局, 重新把元素外观绘制出来的过程,叫做重绘。表现为某些元素的外观被改变



 

触发重排和重绘

1.添加.删除.更新DOM节点.会触发

2.通过display:none 隐藏一个DOM节点

3.通过visibility:hidden 隐藏一个DOM节点

4.添加一个样式表, 调整样式属性

5.用户行为, 例如调整窗口大小, 改变字号, 或者滚动




 

box - sizing属性

box - sizing 规定两个并排的 带有 边框的框,

content - box: 宽度和高度分别应用到元素的内容框, 在宽度和高度之外挥之愿随的内边框和边框(标准盒子模型)

border - box: 将元素设定的宽度和高度决定了元素的边框盒[IE盒子模型]

inherit: 继承父元素的box - sizing



 

工作流程 :提需求 => 然后指定需求文档=>制作原型图 【我们那边是原型prd】=>开会商讨需求【前后端看需求,估算工作量,有问题提出问题】

 =>接口提供接口文档 =>提供设计稿 【如有问题,需要商议】=>开发=>本地自测=>提交给测试测=>测试环境测=>线上环境前一个测试环境测=>上线



 

BFC的概念? 规范是什么? 生成?作用? 有什么缺点?

1.bfc全称Block formatting context,块级格式化上下文,字面理解就是一个独立渲染的区域,里面的内容不受外界的影响。

2.一般设置了浮动,绝对定位 / 固定定位、dispaly的值为inline - block、table - cell、flex…或者元素设置了overflow的值除visible的情况下会产生。

3.一般用于清除浮动、设置左边固定大小,右边自适应布局,margin之间的层叠问题。

4.缺点的话因为我没有找到其他文献,我猜是bfc应该会引起重排,消耗性能吧。

BFC(块级格式上下文)

布局规则

1.内部Box在垂直方向, 一个接一个放置

2.Box垂直方向的距离有margin决定, 数据同一个BFC的两个i相邻Box的margin会发生重叠

3.每个元素的margin box 的左边, 与包含块border box的左边相接触

4.BFC的区域 不会与floatbox重叠()

5.BFC是一个独立容器, 里边子元素不会影响外边的元素()

6.计算BFC高度时, 浮动元素也参与计算高度

7.元素的烈性和display属性, 决定了这个Box类型.

    BFC用处

清除浮动,给父元素添加overflow: hidden。因为计算BFC的高度时,浮动元素也参与计算。

用于两栏自适应布局

防止垂直margin合并

BFC 是一个独立的布局环境, 可以理解为一个容器, 在这个容器中按照一定规则进行物品摆放, 并且不会影响其它环境中的物品。

如果一个元素符合触发 BFC 的条件,则 BFC 中的元素布局不受外部影响。

浮动元素会创建 BFC,则浮动元素内部子元素主要受该浮动元素影响,所以两个浮动元素之间是互不影响的

如何创建BFC

1.根元素

2.float不为none

3.positions为absolute或fixed

4.display的值为inline - block table - cell table - caption

5.overflow的值不为visible



 

元素水平居中

1.行内元素: text - align: center

2.对于有宽度的块级元素

    (1).width和margin实现

    (2).绝对定位和margin - left: (父width - 子width) / 2 前提父元素position为relative

3.宽度未知

    (1).display:inline - block和text - align: center实现水平居中。

(2).绝对定位 + transform: translateX(-50 %)

    (3).flex justify - content: center

垂直居中

1.利用line - height实现居中

2.设置父元素相对定位, 子元素设置绝对定位, 标签通过margin实现自适应居中

3.父元素flex布局 子元素margin: auto实现

4.父级设置相对定位, 子级通过转换为表格形式, 然后子级设置vertical - align实现

5.justify-content:center ; align-item:center

数组方法:

改变原数组方法:

arr.splice()

arr.reverse()

arr.sort()

arr.push()

arr.pop()

arr.shift()

arr.unshifth()

不改变原数组的方法:

arr.slice()

arr.map()

arr.forEach()

arr.every()

arr.some()

arr.filter()

arr.reduce()

arr.find()



 

JS数据类型

JavaScript. 数据类型. 值类型 (基本类型) :字符串(String)、数字 (Number)、布尔 (Boolean)、

对空(Null)、未定义(Undefined)、Symbol。. 引用数据类型 :对象 (Object)、数组 (Array)、函数 (Function)。

1. type of 其中数组.对象.null.都会被判断为Object, 其他判断都正确

2. instanceof 只能判断引用数据类型, 不能判断基本数据类型

// 3.constructor 它有两个作用 一 判断数据类型.二是对象实例通过constructor对象访问它的构造函数.需要注意的事情是如果创建一个对象来改变它的原型, constructor就不能来判断数据类型了

4. Object.prototype.toString.call()



 

var && let && const的区别

1.  var 定义的变量, 没有块的概念, 可以跨块访问, 不能跨函数访问

    let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。

    const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,且变量指向的那个内存地址不能修改。

2.var可以先使用再声明 存在变量提升, let必须先声明在使用

3.var允许在相同的作用域内重复声明同一个变量的, let和const不允许这一现象

4.let 会产生暂时性死区:





 

    JS垃圾回收机制

1.项目中如果存在大量不被释放的内存(堆 / 栈 / 上下文), 页面性能会变得很慢, 会造成内存泄漏.尽可能减少使用闭包, 因为会消耗内存

2.JS具有自动垃圾回收机制, 垃圾收集器定期找出不在继续使用的变量, 然后释放其内存

    (1)标记清除 当变量进入执行环境时, 标记进入环境, 离开执行环境, 会被标记为'离开环境'然后回收所占空间

        (2)谷歌清除: 查找引用 浏览器不定时去查找内存引用, 如果没有被引用, 浏览器会进行回收, 如果被占用就不能回收

            (3)IE浏览器: '引用计数法', 当前内容被占用一次, 计数累加一次, 移除占用 - 1 当为0 就进行回收

3.优化手段: 内存优化; 手动释放: 取消内容的占用

    (1)堆内存: fn = null[null:空]

(2)栈内存: 把上下文中, 被外部占用的堆的占用取消

4.内存泄露

在JS中, 常见的内存泄漏主要4中, 全局变量 闭包 DOM元素的引用 定时器 / 回调函数


 

JS中 this的五种情况

1.作为普通函数执行时, this指向window

2.当函数作为对象的方法被调用, this就会指向该对象

3.构造器调用, this指向返回的这个对象

4.箭头函数不会创建自己的this, 所以它没有自己的this, 它只会在自己作用域的上一层继承this。所以箭头函数中的this的指向在它在定义时一家确定了,之后不会改变。

5.基于Function.prototype上的apply.call.bind.调用  apply接收参数的是数组 call接受参数列表  bind方法通过传入一个对象 返回一个 this 绑定了传入对象的新函数。这个函数的 this指向除了使用new 时会被改变,其他情况下都不会改变。若为空默认是指向全局对象



 

原型 && 原型链

关系:   每个class都有显示原型 prototype

每个实例都有隐式原型_proto_

实例的_proto_指向对象class的prototype

除了最顶层的Object对象没有__proto__, 其他所有的对象都有__proto__, 这是隐式原型

原型链:当我们访问对象身上的某一个属性的时候它会先在自身上访问,自身没有会在它的__proto__上进行访问如果没有就会在构造器函数上prototype访问

这一层层访问的过程就是原型链

总结:

1、当一个对象查找属性和方法时会从自身查找, 如果查找不到则会通过__proto__指向被实例化的构造函数的prototype

2、隐式原型也是一个对象, 是指向我们构造函数的原型

3、除了最顶层的Object对象没有__proto_,其他所有的对象都有__proto__, 这是隐式原型

4、隐式原型__proto__的作用是让对象通过它来一直往上查找属性或方法,直到找到最顶层的Object的__proto__属性,它的值是null, 这个查找的过程就是原型链


 

// es5获取对象得原型

Object.getPrototypeOf(person) === Person.prototype   true



 

setTimeout、Promise、Async / Await 的区别:

settimeout的回调函数会放在宏任务队列, 等到执行栈清空以后执行

2.Promise 本身是同步的立即执行函数,

    3.async / await 函数返回一个Promise对象, 当函数函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。



 

介绍节流    原理、区别以及应用

节流: 事件触发后, 规定时间内, 事件处理函数不能再次被调用, 也就是说在规定时间内, 函数只能调用一次, 且是最先被触发调用的那次

防抖: 多次触发事件, 事件处理函数只能执行一次, 并且是在触发操作结束时执行。也就是说,当一个事件被触发准备执行事件函数前,会等待一定的时间(这时间是码农自己去定义的,比如 1 秒),如果没有再次被触发,那么就执行,如果被触发了,那就本次作废,重新从新触发的时间开始计算,并再次等待 1 秒,直到能最终执行!

使用场景:

节流:滚动加载更多 搜索框输入联想功能 高频点击

防抖: 搜索框输入并且输入后自动搜索.手机号.邮箱验证输入检测 窗口大小resize变化.重新渲染 表单重复提交

节流:

function jl(fn, delay) {

    let lastTime = 0   //记录最后激活事件

    return function () {

        var nowTime = Data.now();

        if (nowTime - lastTime > delay) {

            fn.call(this) //修正this指向并调用

            lastTime = nowTime

        }

    }

}

防抖:

    function fd(fn, delay) {

        let timmer = null

        return function () {

            // 清除上一次的定时器

            clearTimeout(timmer);

            timmer = setTimeout(() => {

                fn.apply(this)

            }, delay);

        }

    }



 

简述MVVM

视图模型双向绑定, 是Model - view - ViewModel的缩写,

    View代表UI组件,ViewModel是View和Model层的桥梁

数据会绑定到viewModel层并自动将数据渲染到页面中,

    视图变化的时候会通知viewModel层更新数据。以前是操作DOM结构更新视图

现在是数据驱动视图

优点:

1.低耦合

2.可复用性

3.独立开发

4.可测试


 

Vue底层实现原理

vue.js 是采用数据劫持结合发布者订阅者模式的方式, 通过Object.defineProperty()来劫持各个属性

的setter和gettter, 在数据变动时发布消息给订阅者,触发相应的监听回调

Observer(数据监听器): Observer的核心是通过Object.defineProprtty()来监听数据的变动, 这个函数内部可以定义setter和getter,

每当数据发生变化, 就会触发setter。这时候Observer就要通知订阅者, 订阅者就是Watcher

Watcher(订阅者):Watcher 订阅者作为Observer和Compile之间通信的桥梁, 主要功能:

1.自身实例化时 将属性订阅器(dep)里添加自己

2.自身必须有一个update()方法

3.带属性变动dep.notice()通知时, 能调用自身的update()方法, 并触发Compile中绑定的回调

Compile(指令解析器): Compile主要做的事情是解析模板指令, 将模板中变量替换成数据, 然后初始化

渲染页面视图, 并将每个指令对应的节点绑定更新函数,添加鉴定数据的订阅者,一旦数据有变动,收到通知,更新试图

computed与watch

watch 属性监听: 是一个对象.键是需要观察的属性, 值是对应回调函数, 主要监听某些特定数据的变化, 从而

进行某些业务操作, 监听的是已经在data中定义的变量, 当变量变化, 触发watch的方法

computed: 计算属性: 属性结果会被缓存.当computed中函数所依赖的属性没有发生变化, 调用当前函数

结果会从缓存中读取  computed中的函数必须要return返回最终的结果 computed更高效, 优先使用, data 不改变, computed 不更新


 

computed: 当一个属性受多个属性影响的时候使用, 例:购物车商品结算功能 watch: 当一条数据影响多条数据的时候使用, 例: 搜索数据出现联想



 

计算属性和watch有什么区别 ? 以及它们的运用场景 ?

computed 计算属性: 依赖其他属性值, 并且computed的值有缓存, 只有他依赖的属性值发生改变, 下一次获取computed的值时才会重新计算computed的值.

watch: 更多是观察的作用, 无缓存性, 类似于某些数据的监听回调, 每当监听的数据发生变化时都会执行回调执行后续操作

运用场景: 当需要进行数据的计算, 并且依赖于其他数据时, 应该使用computed, 因为可以利用computed的缓存属性避免每次获取值都要重新计算

当需要在数据变化时执行异步或开销较大操作时, 应该使用watch, 使用watch选项允许执行异步操作限制执行该操作的频率,并在得到最终结果前,设置中间状态。

为什么v - for和v - if不建议用在一起

因为v -for 比v -if 优先级更高 v -if 会重复执行与每个v - for循环当中

不应该把v -if 和v -for 放在一起

 vue2中v -for 比v -if 优先级更高 v - if会作用于每一个遍历的dom元素上,

 解决这个问题 在最外层嵌套template  在这一层进行v-if判断,然后在内部进行循环

        为了过滤列表中的项目, v -for= "user in users" v -if= "user.isActive"  

 users.filter(u => u.isActive)

为了避免渲染本应该被隐藏的列表(比如 v -for= "user in users" v -if= "sholdShow")

    /* 组件中的data 为什么是一个函数 */

1.一个组件被复用多次的话, 也就会创造多个实例, 本质上, 这些实例用的都是同一个构造函数,

2.如果data是对象的话, 对象数据引用类型, 会影响到所有的实例.所以为了保证组件不同的实例之间

data不冲突, data必须是一个函数

数据以函数返回值的形式定义,这样当我们每次复用组件的时候,就会返回一个新的data,

也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。

vue组件data为什么必须是函数.而vue根实例则没有此限制?

1.data必须是个函数是保证在多实例的时候,为了保证相互之间的状态互不干扰,不污染

2.每次在创建根实例的时候,使用new方法.全局的范围内只创建了一个,不会创建多个,不会存在污染的问题



 

vue组件化的理解

组件化就是把单独的功能提出来,

1.组件是可复用的Vue实例,增加代码的复用性、可维护性和可测试性.提高开发效率 局部刷新



 

key的作用是为了diff算法执行更快的找到对应的节点, 提高diff速度, 更高效的更新虚拟DOM


 

$emit 1.this.$emit('自定义事件名', 要传送的数据)

2.触发当前实例上的事件, 要传递的数据会传给监听器

$on 1.VM.$on('事件名', callback) 回调$emit要传递的数据

2.监听当前实例上自定义的事件

首先在main.js新增bus 作为公共实例

export var bus = new Vue()

  子组件中触发事件 用 @click

lastpage(){

    bus.$emit('listencurrpage', this.curpage)

}

父组件用$on监听事件

bus.$on('listencurpage', (curpage) => {

    this.curpage = curpage

})





 

nextTick的实现

作用: 在下次DOM更新循环结束之后执行的延迟回调   修改数据后立刻得到更新后的DOM结构

1.nextTick 是vue提供的一个全局API, 是在下次DOM更新循环结束之后执行延迟回调, 在修改数据

之后使用$nextTick 可以在回调中获取更新后的DOM

原理:1.定义回调函数存放所有的nextTick里的回调函数

2.若支持promise就用promise 不支持就用   MutationObserver 都不支持可以用setTimeout来完成


 

使用场景: 数据改变后获取焦点 new swiper 必须在页面DOM元素更新完之后调用

showsou(){

  this.showit = true

  this.$nextTick(function () {

    // DOM 更新了

    document.getElementById("keywords").focus()

  })

}


 

插槽: 就是子组件提供给父组件使用的一个占位符, 父组件可以在这个占位符填充任何模板代码

插槽分为: 具名插槽 / 匿名插槽 / 作用域插槽

slot的理解: slot就是插槽, 主要作用是拓展组件, 在重复使用一个组件的时候可以通过少量的修改就达到复用的效果

插槽分为匿名插槽, 具名插槽, 作用域插槽其中前两个都是元素在父组件中, 拓展的结构也在父组件中, 直接在子组件中占位

在父组件中添加结构即可, 区别就是具名插槽给插槽去了名字, 多个插槽存在时可以一一对应, 而作用域插槽的数据在子组件中

拓展的结构要在父组件中, 这时就要利用 子 ====> 父的通信, 给数据一个新的作用域, 因此叫做作用域插槽




 

keep - alive

作用: 实现组件缓存, 保持这些组件的状态 避免出现   反复渲染导致性能浪费

场景: tabs标签页, 后台导航, vue性能优化

原理: Vue.js 内部将DOM节点抽象成了一个个VNode节点 keep - alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。

它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,

    在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。

在被keep - alive包含的组件 / 路由中, 会多出两个生命周期钩子:activated 与 deactivated.

activated 在组件第一次渲染会被调用, 之后在每次缓存组件被激活时调用.

    deactivated:组件被停用(离开路由)时调用

使用了keep - alive就不会调用beforeDestroy(组件销毁前钩子)和destroyed(组件销毁),因为组件没被销毁,被缓存起来了。

最近两周出去面试遇到的面试题(前端初级、长更)



 

前端路由原理

1.SPA 单页面应用.所谓单页web应用, 是加载单个 HTML.页面 并在用户应用程序交互.时 动态更新该页面的web应用程序

浏览器一开始加载必须的HTML CSS Javascript 所有操作都在这张页面上完成






 

git命令常用:

    git add.  上传到暂存区

    git commit  提交到版本库(本地仓库)

    git push    从本地仓库上传到远程仓库

    git pull    从远程仓库拉取最新工作区

    git clone  克隆

    git checkout 查看版本库

git切分支和创建分支怎么弄?

1.git branch 查看当前属于什么分支

2.git branch +名称 创建分支

3.git checkout +名称 切换分支

4.git chckout -b +名称 创建+切换分支

简单 来说  hash 后端不受任何影响 不会出现404 ,而history 需要后台配置支持 如果匹配不到资源就会 报 404


 

1.Hash和history有什么区别 ?

    1.Hash 改变url 不触发页面加载 不利于SEO优化 hash只能修改#后面的部分

2.hash 永远不会提交到服务端  hash 是通过 window.onhashchange 的方式监听hash的改变

3.history 新的url可以是与当前url同源任意url, 也可以与当前url同一地址, 会重复记录栈当中

4.通过 history.state 添加任意类型的数据到记录中

5.可以额额外设置title属性

6.通过pushState replaceState来实现无刷新跳转功能

7.服务端推荐用hash  客户端


 

Object.assign 理解:

1.实现对象的合并  语法: Object.assign(target, ...sources)

解析:

1.Object.assign会将source里面的可枚举属性复制到target, 如果已经有属性重名会被覆盖

2.后续source会覆盖之前的source的同名属性

3.赋值的是属性值, 如果属性值是一个引用类型, 那么复制的其实是引用地址, 就会存在引用共享问题


 

浅拷贝与深拷贝

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

就会影响到另一个对象.

    深拷贝: 就是将一个对象从内存中完整的拷贝一份出来, 从堆内存中开辟一个新的区域, 存放新对象

且修改新对象不会影响原对象

浅拷贝实现方式: Object.assign() 可以把任意多个源对象自身的可枚举属性拷贝给目标对象 返回目标对象

例: let obj1 = Object.assign({}, obj1)

 展开运算符 ...

Array.prototype.concat()

Array.prototype.slice()


 

深拷贝:

JSON.parse(JSON.stringify())

2.递归

function copy(obj) {

    var newobj = null;

    //通过typeof判断是否为引用数据类型且不等于null

    if (typeof (obj) === 'object' && obj !== null) {

        //三元表达式 来判断储存的类型

        newobj = obj instanceof Array ? [] : {}

        //循环obj中的每一项,如果里面还有复杂数据类型,则直接递归再次调用copy函数

        for (const key in obj) {

            newobj[key] = copy(obj[key])

        }

    } else {

        newobj = obj

    }

    return newobj

}


 

 axios 请求拦截器 && 响应拦截器

1.请求拦截器:

在请求发送前可以进行操作, 例如添加--cookie 请求体加验证 设置请求头等等, 相当于是对每个接口相同操作的一个封装

2.相应拦截器:

 同理 请求得到响应之后, 对响应体的一些处理, 通常是数据统一处理等, 也常来判断登陆失效


 

 创建 实例

import axios from 'axios'

//  let instance = axios.create({

//     baseURL:'',

//     timeout:5000

//  })

let baseURL;

if (process.env.NODE_ENV === 'development') {

    baseURL = ''

} else {

}

let instance = axios.create({

})

请求拦截器

instance.interceptors.request.use(req => {

    config.headers['X-Requested-With'] = 'XMLHttpRequest';

}, err => { });

响应拦截器

instance.interceptors.response.use(req => {

}, err => { })

export {

    instance

}




 

//

箭头函数和普通函数区别

    (1).箭头函数更简洁

    (2).箭头函数没有自己的this: 不会创建自己的this, 他只会在自己作用域的上一层继承this 所以箭头函数中和this指向在它定义时已经确定

        (3).箭头函数继承来的this永远不会改变

        (4).call() apply() bind() 等方法不会改变箭头函数的this指向

            (5).箭头函数不能作为构造函数使用

            (6).箭头函数没有自己的arguments

            (7).箭头函数没有prototype;

//

什么是闭包 ? 闭包的作用

当一个内部函数被调用, 就会形成闭包, 闭包就是能够读取其他函数内部变量的函数

作用: 局部变量无法共享和长久保存, 而全局变量可能造成变量污染, 为了让数据长久保存 并且不被全局污染

函数嵌套函数 并且 内部函数调用了外部的变量



 

Promise是什么 ?

    Promise是异步编程的一种解决方案 : 从语法上来说, promise是一个对象, 从他可以获取异步操作的消息; 从本意上将,

        他是承诺, 一段时间给出结果  三种状态: pending(等待中), fulfiled(成功), rejected(失败): 一旦改变不会再改变

map 和forEach区别

forEach()方法针对的每一个元素执行提供的函数, 该方法没有返回值, 是否改变元数据取决于数组元素的类型是基本类型还是引用数据类型

map()不会改变原数组的值 返回一个新的数组, 新数组的值为原数组调用函数处理之后的值


 

UDP和TCP有什么区别

无连接, 面向链接

不可靠传输, 可靠传输

支持一对一, 一对多, 多对多交互, 只能一对一通信

面向报文, 面向字节流

开销小8     20 - 60

场景: 实时应用: 例如视频会议, 直播          适用于要求可靠传输的应用, 例如文件传输



 

v - model 双向数据绑定原理

vue中双向绑定是一个指令v-model,可以绑定一个响应式数据到视图,同时视图中变化能改变该值。

v-model是语法糖,默认情况下相当于:value和@input。使用v-model可以减少大量繁琐的事件处理代码,提高开发效率。

默认情况相当于:value 和@input 使用v-model可以减少大量的事件和处理代码,提高开发效率


 

forEach 缺点: 不能同时遍历多个集合, 遍历时候无法修改和删除集合数据

            无法应用于空数组 方法不能使用break,continue语句跳出循环

            强制结束循环:

            let arr = [1, 2, 3]

try {

    arr.forEach(item => {

        if (item === 2) {

            throw('循环终止')

        }

        console.log(item)

    })

} catch(e) {

    console.log('e: ', e)

}

for  in (多用于遍历对象json): 可以遍历对象  不建议遍历数组

for of 避免了 for in的缺点, 可以使用 break, continue 和return 跳出支持数组遍历, 还可以遍历类似数组的对象, 支持字符串遍历

缺点: 不适用于处理原有的原生对象



 

说一下for...in 和 for...of的区别 ?

 for in 获取的是对象的键名,for of 遍历获取的时对象的键值

for in 会便利对象的整个原型链, 性能差 而for of 之便利当前对象不会遍历原型链

对于数组的遍历 for in 会返回数组中所有可枚举的属性(包括原型链), for of 只返回数组的下标对应的属性值

总结: for ... in 循环只要是为了遍历对象而生, 不适用数组,

 for....of循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象

Vuex有哪些基本属性 ? 为什么Vue的mutation中不能做异步操作 ?

    5中: 分别是 State, Getter, Mutation, Action, Moudule

1.state => 基本数据(数据源存放地)

2.getters => 从基本数据派生出来的数据(相当于处理数据)

3.mutations => 提交哦更改数据的方法, 同步

4.actions => 像一个装饰器, 包裹mutations, 进行异步操作

5.modules => 模块化Vuex

1.Vuex中所有状态更新的唯一途径就是mutation, 通过Actions去提交mutation实现,

    这样可以方便跟踪每一个状态的变化, 从而能够实现开发工具帮助更好的了解我们的应用

2.每个mutation执行完成后都会对应一个新的状态变更

如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。


 

vuex的使用场景

1.可以实现跨组件通信

22.组件之间的传值有几种方式 ?

    1.父传子

2.子传父

3.eventbus

4.ref / $refs

5.$parent / $children

6.$attrs / $listeners

7.依赖注入(provide / inject)


 

根据组件关系:

父子组件:

props / $emit / $parent / ref / $attrs

兄弟组件:

$parent / eventbus / vuex

跨层级关系

eventbus / vuex / provide + inject


 

23. import Vue form 'vue'

export const bus = new Vue()

    (2)--  Vue.prototype.$bus = new Vue()

A页面: this.$bus.$emit('aMsg', '来自A页面消息')

B页面: this.$bus.$on('aMsg', (msg) => {

    this.msg = msg

})

or import { bus } form 'event-bus.js'

bus.$emit('aMsg', '来自A页面的消息') ...





 

父组件到子组件更新的方式是什么样的

1. 父组件修改子组件的data, 并实时更新

子组件通过$emit 传递子组件的数据, this.$data指当前组件的data(return { ...}) 里的数据

this.$emit('data', this.$data);

之后通过父组件的自定义事件来接受

@data = 'getinputdata'

其中data就是传过来的数据, 通过修改这个数据就可以通过父组件实时更新子组件

getinputdata(data) {

    console.log(data);

    data.background = {

        backgroundColor: 'yellow',

        border: 'none'

    };

}


 

2.子组件中修改父组件的data

只能通过$emit来触发父组件中的方法 去修改

3.子组件获取父组件的data, 修改单不实时更新-- - 父传子

    (1) 子组件将父组件通过 props传递的数据, 再把props的值赋给let 或var声明变量, 然后使用这个变量

        (2) 子组件将父组件通过props传递的数据, 再把props的值赋给data(return { ...}里的变量)

4.父组件获取子组件的data, 修改但不实时更新-- - 子传父

子组件通过$emit把值传给父组件。




 

全局守卫

vue - router 全局有三个守卫:

1.router.beforeEach 全局前置守卫, 进入路由之前

2.router.beforeResolve 全局解析守卫 在beforeRouteEnter调用之后调用

3.router.afterEach 全局后置守卫 进入路由之后

to, from, next 这三个参数:

to和from是将要进入和将要离开的路由对象, 路由对象指的是平时通过this.$route获取到的路由对象。

next:Function 这个参数是个函数,且必须调用,否则不能进入路由(页面空白)

next() 进入该路由

next(false) 取消, url重置为form 路由地址(也就是将要离开的路由地址)

next 跳转新路由, 当前的导航被终端, 重新开始一个新的导航

路由独享守卫

const router = new VueRouter({

    routes: [

        {

            path: '/foo',

            component: Foo,

            beforeEnter: (to, from, next) => {

                // 参数用法什么的都一样,调用顺序在全局前置守卫后面,所以不会被全局守卫覆盖

                // ...

            }

        }

    ]

})

路由组件内的守卫

1.beforeRouteEnter 进入路由前     // 在路由独享守卫后调用 不能获取组件实例 this,组件实例还没被创建

2.beforeRouteUpdate 路由复用同一个组件

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

// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,

// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。

3.beforeRouteLeave 离开当前路由

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




 

自定义指令:

注册一个自定义指令有全局和局部

全局主要通过 Vue.directive方法进行注册

Vue.directive('参数名字', {

    // 当被绑定的元素插入到 DOM 中时……

    inserted: function (el) {

        el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能

    }

})

场景: 防止表单重复提交

Vue.directive('throttle', {

    bind: (el, binding) => {

        let throttleTime = binding.value; // 节流时间

        if (!throttleTime) { // 用户若不设置节流时间,则默认2s

            throttleTime = 2000;

        }

        let cbFun;

        el.addEventListener('click', event => {

            if (!cbFun) { // 第一次执行

                cbFun = setTimeout(() => {

                    cbFun = null;

                }, throttleTime);

            } else {

                event && event.stopImmediatePropagation();

            }

        }, true);

    },

});

// 2.为button标签设置v-throttle自定义指令

<button @click="sayHello" v - throttle > 提交</button >




 

    rem 适配:

rem是相对于根节点html 的font - size来计算的

vh vw 是根据视图窗口进行适配

媒体查询:

是css3 开始加入的一个功能, 它可以进行响应式适配   print 用于打印机和打印预览

1.一个可选的媒体类型(如 screen.print 等)  screen 用于电脑屏幕, 平板电脑, 智能手机

2.零个或多个媒体功能限定表达式(如 max - width: 500px)

@media

首页白屏:

JS文件打包后文件太大 webpack界面太多 都打包在一个js文件 调用接口会加载这个JS文件 造成白屏现象

首屏性能优化你是怎么做的 ?

    骨架屏插件 把布局做成一个组件, 需要页面进行引用, 然后等数据请求回来

隐藏骨架屏 显示正常页面, 通常仅用于接口较多的页面

路由懒加载

白屏问题

1.在接口加loading 动画,

    2.骨架屏

    3.cdn加速  

    4.路由懒加载

    5.图片懒加载



 

说一下slice splice split 的区别

slice(start, [end]) 包含了源函数从start到 end 所指定的元素,但是不包括end元素,比如a.slice(0, 3); 返回一个新的数组

如果出现负数 就把负数与长度相加后再划分

slice中的负数的绝对值若大于数组长度就会显示所有数组

如果餐厨只有一个, 并且参数大于length, 则为空

如果结束位置小于起始位置, 则返回空数组

返回个数是end - start的个数

不会改变原来的数组

// 个人总结:slice的参数如果是正数就从左往右数,如果是负数的话就从右往左边数,

// 截取的数组与数的方向一致,如果是2个参数则截取的是数的交集,没有交集则返回空数组

// ps:slice也可以切割字符串,用法和数组一样,但要注意空格也算字符

splice(start, deletecount, item)

start: 起始位置

deletecount: 删除位数

item:替换  改变原来的数组


 

split(字符串)

// string.split(separator,limit):split方法把这个string分割成片段来创建一个字符串数组。

// 可选参数limit可以限制被分割的片段数量。

// separator参数可以是一个字符串或一个正则表达式。

// 如果separator是一个空字符,会返回一个单字符的数组,不会改变原数组。

// 注意:String.split() 执行的操作与 Array.join 执行的操作是相反的。

怎么把类数组转化为数组

通过Array.form方法来实现转换

Array.form(arrayLike)

通过call调用数组的slice方法

Array.prototype.slice.call(arrayLike)

通过call调用数组的splice方法来实现

Array.prototype.splice.call(arrayLike, 0)

通过apply调用数组的concat方法来实现转换

Array.prototype.caoncat.apply([], arrayLike)


 

说一下怎么取出数组最多的一项

示例:

const d = {};

let ary = ['赵', '钱', '孙', '李', '周', '李', '周', '周', '李'];

ary.forEach(k => arr[k] ? d[k] = 1 : d[k]++);

const result = Object.keys(d).sort((a, b) => d[b] - d[a]).filter((k, i, l) => d[k] === d[l[0]])

console.log(result);



 

说一下JOSN.stringify 有什么缺点

1.如果obj里面有时间对象, 则JSON.stringify后再JSON.parse的结果, 事件将是字符串形式而不是对象形式

2.如果obj有正则表达式, Error对象 序列化结果为空

3.如果obj里有函数, undefined, 则序列化的结果会把函数或undefined丢失

4.如果obj有NaN, Infinity和 - Infinity 则序列化结果会变成null

5.JOSN.stringify()只能序列化对象的可枚举的自有属性; 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后, 会丢弃对象的constructor;

6.如果对象中存在循环利用的情况将无法正确实现深拷贝



 

SPA有什么优缺点:

1.体验好, 不刷新, 减少http请求 数据ajax异步获取

2.前后端分离

3.减轻服务端压力

4.共用一套后端程序代码 适配多端

缺点: 1.首屏加载慢

2.SEO不利于搜索引擎抓取


 

61.说一下前端登陆的流程:

初次登陆的时候, 前端调后端接口, 发送用户名和密码验证用户名和密码, 验证成功, 就给前端返回一个token,

    和一个用户信息的值, 前端拿到token, 将token储存到Vuex中, 然后从Vuex中把token的值存入浏览器Cookies中。把用户信息存到Vuex然后再存储到LocalStroage中,

        然后跳转到下一个页面,根据后端接口的要求,

只要不登录就不能访问的页面需要在前端每次跳转页面时判断Cookies中是否有token,

    没有就跳转到登录页, 有就跳转到相应的页面, 我们应该再每次发送post / get请求的时候应该加入token,

常用方法再项目utils / service.js中添加全局拦截器,

    将token的值放入请求头中 后端判断请求头中有无token,

        有token 就拿到token并验证token是否过期,

            在这里过期会返回无效的token然后有个跳回登录页面重新登录并且清除本地用户的信息

页面访问权限, 两种方式, 直接将路由表保存在服务端, 通过登陆返回对应的路由表

给路由设置守卫, 通过用户的权限列表 将对应的路由添加到路由表中

状态码: 由3为数组, 第一个数字定义了相应类别

1xx: 指示消息, 标识请求已接收, 继续处理

2xx: 成功, 标识请求已经被接受

3xx: 重定向

4xx: 客户端错误

5xx: 服务器端错误, 服务器未能实现合法的请求


 

iframe 有什么优点.缺点

优点

1.iframe能够原封不动的将嵌入的网页展示出来

2.加载慢的第三方内容, 如图标或广告, 这些可以由iframe来解决

缺点

1.阻塞主页面的onload事件

2.增加服务器http请求

3.兼容性差

4.用户体验差,



 

    null和undefined的区别 如果让一个属性变为null

undefined是一个变量自然的.最原始的状态值, 而null是一个变量被认为的设置为空对象

undefined 违背定义的值

1.声明变量未赋值

2.访问对象上不存在的属性

3.定义了形参, 但是没有传递

4.使用void对表达式求值

二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。



 

加载渲染过程

父beforeCreate -> 父created -> 父beforeMount -> 子 beforecreate -> 子 created -> 子beforeMount -> 子mounted -> 父mounted



 

Vue 首屏渲染优化

1.图片懒加载

2.cdn 引入公共库

3.路由懒加载

4.骨架屏

Vue 的优点和缺点

1.渐进式框架: 可以在任何项目中引入

2.双向数据绑定: 操作更简单

3.组件化: 很大程度实现了逻辑的封装和重用

4.视图, 数据, 结构分离




 

怎么理解Vue的单向数据流

所有prop都使得父子prop之间形成了一个单向下行绑定: 父级prop的更新会向下流动到子组件中, 但是反过来不行

防止子组件意外的更改了父组件的状态, 导致应用数据流难以理解

只能通过$emit 去派发一个自定义事件 父组件收到后 由父组件修改


 

如何拓展一个组件:

逻辑扩展有:slots  extends componsition api

 内容扩展有:mixins  

  混入缺点: 数据和方法不能明确判断来源, 且可能和当前组件内变量名产生冲突


 

混合是一种分发vue组件中可复用功能非常灵活的方式

混入对象可以包含任意组件选项

vue.use的作用是通过全局方法 Vue.use()使用插件


 

1.响应式就是: 能够使数据发生变化可以被检测并对这种变化做出响应的机制

2.MVVM框架中 通过数据驱动应用, vue通过数据响应式加上虚拟DOM 开发人员只需要操作数据, 关心业务, 不用繁琐的操作DOM 提高开发效率

3.vue2 如果是对象就采用Object.defineProperty()的方式定义数据拦截, 当数据被访问或发生变化, 我们感知并做出响应, 如果是数组则通过覆盖数组对象原型的7个变更方法

使这些方法可以额外的做更新通知,从而作出响应


 

虚拟DOM的理解:

虚拟dom就是虚拟dom对象, 它本身就是一个JavaScript对象, 将真实元素节点抽象成VNode有效减少直接操作dom次数, 从而提高程序性能

挂在过程结束后, vue程序进入更新流程, 如果某些响应式数据发生变化, 新的虚拟dom会和上一次的渲染结果比对, 从而转化为最小量的dom操作, 高效更新视图

直接给一个数组项赋值, Vue能检测到变化么 ?

    由于JavaScript的限制, Vue不能检测到以下数组的变动:

当你利用索引直接设置一个数组项时, 例如: vm.items[indexOfItem] = newValue

当你修改数组的长度时, 例如: vm.items.length = newLength

为了解决一个问题.Vue提供以下操作方法:

Vue.set(vm.items, indexOfItem, newValue)

vm.$set(vm.items, indexOfItem, newValue)

//Array.prototype.splice

vm.items.splice(indexOfItem, 1, newValue)

在哪个生命周期内调用异步请求 ?

    可以在钩子函数 created, beforeMount, mounted 内进行调用, 因为在这三个钩子函数中, data已经创建

推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

能更快获取到服务端数据,减少页面 loading 时间;

ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;


 

在什么阶段才能操作DOM ?

    1.mounted被调用前, Vue已经将编译好的的模板挂载到页面上, 所以在mounted中 可以访问操作DOM

beforeCreate:是 new Vue() 之后触发的第一个钩子,在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问。

created:在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发 updated 函数。可以做一些初始数据的获取,

 在当前阶段无法与 Dom 进行交互,如果非要想,可以通过 vm.$nextTick 来访问 Dom。

beforeMount:发生在挂载之前,在这之前 template 模板已导入渲染函数编译。而当前阶段虚拟 Dom 已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发 updated。

mounted:在挂载完成后发生,在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom 节点,使用 $refs 属性对 Dom 进行操作。

beforeUpdate:发生在更新之前,也就是响应式数据发生更新,虚拟 dom 重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。

updated:发生在更新完成之后,当前阶段组件 Dom 已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。

beforeDestroy:发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。

destroyed:发生在实例销毁之后,这个时候只剩下了 dom 空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。






 

父组件可以监听到子组件的生命周期吗?

当然 @hook 方法不仅仅是可以监听 mounted, 其它的生命周期事件, 例如: created, updated 等都可以监听。


 

keep - alive的了解

它Vue内置的一个组件, 可以使被包裹的组件保留状态, 避免重复渲染

1.一般结合路由和动态组件一起使用, 应用于缓存组件

2.提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;

3.对应两个钩子函数 activated 和deactivated, 当组件被激活时, 触发钩子

activated 当组件被激活时, 触发钩子函数activated, 当组件被移除时, 触发钩子函数deactivated




 

你是用过vuex么 ?

    Vuex是一个转为Vue.js应用程序开发的状态管理模式.每个Vuex应用的核心就是store

包含了你的应用中大部分的状态(state)

1.Vuex的状态存储时响应式的.当Vue组件从store中读取状态的时候, 若store的状态发生变化, 那么响应的组件也会相应的得到高效更新

2.改变store中的状态的唯一途径就是显式提交(commit)mutation.这样使得我们可以方便地跟踪 每一个状态的变化

模块:

1.State: 定义了应用状态的数据结构, 可以在这里设置默认的初始状态

2.Getter: 允许组件从Store获取数据, mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性

3.Mutation: 是唯一更改store中状态的方法, 并且必须是同步函数

4.Action: 用于提交mutation, 而不是直接变更状态, 可以包含任意异步操作

5.Module:允许将单一的Store拆分为多个store 且同时保存在单一状态树中



 

1.hash模式的实现原理:

1.URL中hash值值时客户端的一种状态, 也就是说当向服务器发出请求时, hash部分不会被发送

2.hash值的改变, 都会在浏览器的访问历史中增加一个记录.因此我们能通过浏览器的回退, 前进按钮控制hash的切换

3.可以通过a标签, 并设置href属性跳转

4.我们可以同挂hashchange来监听hash值的变化, 从而对页面进行跳转



 

Vue是如何实现数据双向绑定的

Vue数据双向绑定主要是指: 数据发生变化更新视图, 视图变化更新数据

1.输入框内容变化, Data中的数据同步变化

2.Data中的数据变化, 文本节点的内容同步变化

Vue主要通过4步来实现数据双向绑定:

实现一个监听Observer: 对数据对象进行遍历, 包括子属性对象的属性, 利用Object.defineProperty()对属性都加上setter和getter

实现一个Compile解析器: 解析Vue模块指令, 将模板的变量替换成数据, 渲染视图,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。

实现一个订阅者: Watcher: Watcher订阅者是Observer和Compile之间通信的桥梁, 主要任务是订阅Observer中的属性值变化的消息, 当收到属性值变化, 触发解析器Compile

实现一个订阅器Dep: 订阅器采用发布 - 订阅设计模式, 用来手机订阅者Watcher, 对监听器Observer和订阅者Watcher进行统一管理



 

通过 Object.defineProperty() 对数据进行劫持,但是  Object.defineProperty() 只能对属性进行数据劫持,不能对整个对象进行劫持,

同理无法对数组进行劫持,但是我们在使用 Vue 框架中都知道,Vue 能检测到对象和数组(部分方法的操作)的变化,那它是怎么实现的呢?

Vue框架是通过遍历数组和递归遍历对象, 从而达到利用Object.defineProperty()也能对对象和数组进行监听

响应式原理:

Vue在组件和实例初始化的时候,会将data里的数据进行数据劫持(object.definepropty对数据做处理)。被解除过后的数据会有两个属性:一个叫getter,一个叫setter。

getter是使用数据的时候触发,setter是在修改数据的时候触发,修改数据的时候触发setter,同时也触发了底层的watcher监听,通知dom修改刷新。


 

Proxy 与Object.defineProperty 优劣对比

优势:

 1.Proxy 可以直接监听对象而并非属性

2.Proxy可以直接监听数组的变化

3.Proxy有多达13中拦截方法, 不限于apply.ownKeys.deleteProperty.has.等等都是

4.Proxy返回的是一个新对象, 我们可以执行操作新对象达到目的, Object.defineProperty只能遍历对象属性直接修改



 

vm.$set的实现原理:

1.如果目标是数组.直接使用数组的splice方法触发响应式

2.如果是对象, 会先判断段属性是否存在.对象是否是响应式, 最终如果要对属性进行响应式处理, 则是通过调用defineReactive方法进行响应式处理

defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法


 

虚拟DOM的优缺点

1.保证性能下限: 不用粗暴的操作DOM, 提供不错的性能

2.无需手动操作DOM: 不用频繁操作DOM, 只需要写好view - model代码逻辑 提高开发效率

3.跨平台:方便跨平台操作 例如: 服务器渲染

缺点:

无法进行极致优化




 

虚拟DOM的实现原理:

1.用javaScript对象模拟真实DOM树, 对真实DOM进行抽象

2.diff算法 比较两个虚拟DOM树的差异

3.path算法 - 将两个虚拟DOM对象的差异应用到真正的DOM树


 

你对Vue项目进行哪些优化 ?

    !1 代码层面优化

1.v -if 和 v - show区分使用场景

2.computed 和 watch 区分使用场景

3.v -for 必须添加key 并且避免使用v -if

4.图片, 路由懒加载

5.事件监听 销毁

web技术优化:

浏览器缓存 cdn加速



 

vue - 路由守卫 + token过期处理

router.beforeEach((to, form, next) => {

    const isLogin = localStorage.token ? ture : false

    if (to.path == 'login' || to.path == 'register') {

        next

    } else {

        isLogin ? next() : next('login')

    }

})

//请求头处理

axios.interceptors.request.use(config => {

    startLoading()

    //设置统一请求头

    if (localStorage.token) {

        config.headers['token'] = getToken();

        return config

    }

},

    (error) => {

        console.log(error);

        return Promise.inject(error)

    })

// 响应头处理

axios.interceptors.response.use(res => {

    endLoading()

    return res

}, error => {

    endLoading()

    Message.error(error.response.data)

    const { status } = error.response

    if (status == 401) {

        Message.error('token失效,请重新登录')

        //清除token

        localStorage.removeItem('token')

        重新跳转login页面

        router.push('/login')

    }

    return Promise.inject(error)

})

浏览器输入URL的时候, 发生了什么 ?

回答:1.DNS解析IP地址

2然后建立Tcp链接  

3浏览器发送http请求

4服务器响应请求返回数据 关闭tcp链接  

5浏览器渲染  js渲染

6链接结束



 

1.浏览器分析链接指向的URL

2.浏览器向DNS请求解析链接的IP地址

3.DNS服务器解析出的IP地址

4.浏览器与服务器建立TCP链接

5.浏览器发出HTTP 请求

6.服务器通过HTTP响应把文件发送给浏览器

7.释放TCP连接

8.浏览器解析文件, 并将web网页显示给客户

前端面试汇总100题

4.XML和JSON的区别

1.数据体积方面:JSON相对于XML来讲,数据的体积小,传递的速度更快些

2.数据交互方面:JSON与javascript的交互更方便,能够更好的数据交互

3.数据描述方面:JSON对数据的描述性比XML较差

4.传输速度方面:JSON的速度远远快于XML

理解JavaScript中的执行上下文和执行栈

执行上下文:就是评估和执行javaScript代码的环境的抽象概念.每当javascript代码在运行的时候,它都是在执行上下文中运行.


 

v-model 和.sync修饰符本质没有区别:监听一个触发时间 =(val) = value = val

细微之处区别

1.只不过 v-model 默认对应的是 input 或者textarea等组件的input事件,如果子组件替换这个input事件,其本质

和.sync一模一样 比较单一,不能多个

2.一个组件可以多个属性用.sync修饰符,可以同时双向绑定多个'prop',不像v-model  一个组件只能有一个


 

作用场景:

1.v-model针对更多的时最终操作结果,是双向绑定的结果,是value,是一种change操作.比如输入框的值,多选框value值列表等

2..sync针对更多的是各种各样的状态,是状态的互相传递,比如组件loading状态.子菜单和树结构展开列表.某个表单内部验证

使用两种不同的方式双向绑定,能够让我们快速理解组件的结构。



 

HTML:

伪类:用于已有元素处于某种状态时,为其添加对应的样式,这个状态是根据用户行为而动态变化的

伪元素:用于创建不在DOM树中的元素,并为其添加样式


 

微信小程序:

登陆流程

1.调用wx.login 获取小程序的登陆凭证code  

2.通过接口向后端发送code 换取token

3.调用wx.getUserprofile() 获取到加密信息再将加密数据通过后端接口返回给后端,

之后拿到用户的openId或是unionId,确认用户身份。



 

1.通过  wx.login()  获取到用户的code判断用户是否授权读取用户信息,调用wx.getUserProfile 读取用户数据。

2.由于小程序后台授权域名无法授权微信的域名,所以需要自身后端调用微信服务器获取用户信息。

3.通过 wx.request() 方法请求业务方服务器,后端把 appid , appsecret  和 code 一起发送到微信服务器。

appid 和 appsecret 都是微信提供的,可以在管理员后台找到。

4.微信服务器返回了 openid 及本次登录的会话密钥 session_key。

5.后端从数据库中查找 openid ,如果没有查到记录,说明该用户没有注册,如果有记录,则继续往下走。

6.session_key 是对用户数据进行加密签名的密钥。为了自身应用安全,session_key 不应该在网络上传输。

7.然后生成 session并返回给小程序。

8.小程序把 session 存到  storage 里面。

9.下次请求时,先从 storage 里面读取,然后带给服务端。

10.服务端对比 session 对应的记录,然后校验有效期。


 

下拉刷新:

1.在.json文件设置:enablePullDownRefresh:true 用于开启页面下拉刷新效果

2.调用onPullDownRefresh



 

多处组件复用;通过传参不同,实现多处复用  slot进行组件拓展




 

Vue2 中数据变化但视图没有发生同步更新,描述一下出现的场景,解决方式,以及该问题发生的可能原因

这是因为如数组直接用下标直接赋值 或修改数组长度 在 vue2 中 vue 实例的 data 数据是响应式 的(就是数据变了 视图也会跟着变),

⽽我们新增的属性并不是响应式的,由于受现在JS的限制,Vue⽆法检测到属性的新增或删除。所以有时⽆法实时的更新到视图上。

解决方法 可以用$set  或 splice

浏览器根据请求结果的缓存机制来判断是否需要缓存  

 协商缓存是 :强制缓存失败,浏览器带着 缓存标识去请求服务器,有服务器决定是否使用该缓存

 强缓存:与服务器协商后,如果是强缓存,则直接从本地取数据(内存、硬盘)。




 

 封装组件意义:

 可以模块化,分工明确

 代码复用方便

 易于调试

 vue.use 有什么作用

 vue提供了 Vue.use 的全局api来注册插件 另外也可以检查插件是否注册,注册过不需要重复注册

 2.未注册,调用插件install方法 如果是函数,则直接当做install方法调用), 同时将Vue作为第一个参数传入



 

 面向对象

 :可以说是一种对现实事物的抽象,将一类事物抽象成一个类


 

 vuex 使用场景

 Vue一般是单项数据流,于是:

当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

多个视图依赖于同一状态。

涉及非父子组件之间跨组件共享数据

来自不同视图的行为需要变更同一状态。


 

生命周期-destroy里面会做哪些操作

清除监听属性,子组件,事件监听器(只包含自定义)


 

权限管理:

权限管理一般需求是页面权限和按钮权限的管理


 

具体实现的时候分后端和前端两种方案:

前端方案会把所有路由信息在前端配置,通过路由守卫要求用户登录,用户登录后根据角色过滤出路由表。比如我会配置一个asyncRoutes数组,需要认证的页面在其路由的meta中添加一个roles字段,等获取用户角色之后取两者的交集,

若结果不为空则说明可以访问。此过滤过程结束,剩下的路由就是该用户能访问的页面,最后通过router.addRoutes(accessRoutes)方式动态添加路由即可。

按钮权限的控制通常会实现一个指令,例如v-permission,将按钮要求角色通过值传给v-permission指令,在指令的moutned钩子中可以判断当前用户角色和按钮是否存在交集,有则保留按钮,无则移除按钮。


 

纯前端方案的优点是实现简单,不需要额外权限管理页面,但是维护起来问题比较大,有新的页面和角色需求就要修改前端代码重新打包部署;服务端方案就不存在这个问题,通过专门的角色和权限管理页面,配置页面和按钮权限信息到数据库,应用每次登陆时获取的都是最新的路由信息,可谓一劳永逸!

   

EventLoop 事件循环,是解决JavaScript单线程运行阻塞的一种机制



 

vue-router 是什么?它有哪些组件?

vue-router 是Vue.js官方的路由管理器 有router-link、router-view      

active-class是那个组件的属性

vue-router模块的router-link组件。children数组来定义子路由  


 

.$route 和 $router 的区别是什么?

$router时VueRouter的实例,使用$router.push方法。返回上一个历史history用$router.to(-1)

$route时在前router跳转对象,里面可以获取当前路由的name,path,query,params


 

vue-loader是什么?用途有哪些?

vue文件的一个加载器, 作用是解析和转换vue文件  

Vue-router跳转和location.href有什么区别\

href跳转刷新了页面  router无刷新页面  静态跳转

引进router 使用了diff算法   ,按需加载减少了dom消耗

郑州面试欠缺:

JS是单线程的

栈:基本数据类型

堆:引用数据类型

区别:  1.基本数据类型直接存储在栈重,占据空间小,大小固定,属于被频繁使用数据,所以放在栈重

2.引用数据类型存储堆重的对象,占据空间大,大小不固定.如果存储在栈中,将会影响程序运行的性能

引用数据类型在栈中存储了指针.该指针指向堆中该实体的起始地址

1、visibility具有继承性,给父元素设置visibility:hidden;子元素也会继承这个属性。但是如果重新给子元素设置visibility: visible,则子元素又会显示出来。这个和display: none有着质的区别

2、visibility: hidden不会影响计数器的计数,如图所示,visibility: hidden虽然让一个元素不见了,但是其计数器仍在运行。这和display: none完全不一样

 // 为了在删除最后一页的最后一条数据时能成功跳转回最后一页的上一页

        const totalPage = Math.ceil((this.count - 1) / this.pageSize) // 总页数

        this.currentPage = this.currentPage > totalPage ? totalPage : this.currentPage

        this.currentPage = this.currentPage < 1 ? 1 : this.currentPage



 

h5有哪些更新?

1.语义化标签

2.媒体标签 audio音频

3.input type属性

4.进度条,度量器

5.document.querySelector("s1") document.querySelectorAll("s1")

6.存储

css有哪些:

圆角  媒体查询   动画  阴影 盒模型 过渡 动画

uniapp面试题

应用生命周期

onLaunch  当uniapp应用初始化完成时触发,全局只触发一次     一般用于查看用户是否授权 获取用户的设备信息等

onShow  当应用启动,,或从后台进入前台显示时触发,可以触发多次     一般用于重新进入应用的消息提示或者数据刷新

onHide  监听应用从前台进入后台 一般用于退出应用时的消息提示

onError 应用报错时被触发    用于监测并处理错误



 

页面生命周期

onLoad   页面加载出发传递参数  只调用一次

onShow   页面显示触发  

onReady   页面初次渲染完成后触发,一个页面只调用一次

onHide    页面与i内藏触发 每次隐藏触发

onUnload  页面卸载触发

onResize  页面每次窗口尺寸变化触发

小程序页面只能跳转十层  require路径不支持绝对路径

首先调用 uni.login() 方法,获取到code / authCode

然后将这个code 发送给后端,后端会用这个code去和wx / zfx 要 open_id 和 session_key / userid 和 token

然后后端将获取到的这些发给前端,前端用这两个东西,可以干其他的事情

将 金额 和 openid通过一个接口发送给后端,然后后端去进行签名 加密 等等,最后返回我们需要的请求参数


 

1.常用的选择器有哪些?

标签选择器 类选择器 ID选择器 后代选择器 子元素选择器 相邻兄弟选择器 通用选择器 属性选择器

2.权重

从高到低为:!important > 行内样式 > ID选择器 >类选择器 属性选择器 伪类 >标签选择器和伪元素选择器

</script>



 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值