前端面试题套餐没事瞧瞧

有没有用过预处理语言less sass

预处理语言增加了变量,函数,混入等强大功能
@global:#eee
.box{
color:@global
}

判断数据类型的方法

typeof
优点:能够快速区分基本数据类型
缺点:不能将Object、Array和Null区分,都返回object

typeof的作用?

区分数据类型,可以返回7种数据类型:number、string、boolean、undefined、object、function ,以及 ES6 新增的 symbol

typeof 能正确区分数据类型吗?
不能。对于原始类型,除 null 都可以正确判断;
对于引用类型,除 function 外,都会返回 “object”

typeof 注意事项
typeof 返回值为 string 格式,注意类似这种考题: typeof(typeof(undefined)) -> “string”
typeof 未定义的变量不会报错,返回 “undefiend”
typeof(null) -> “object”: 遗留已久的 bug
typeof无法区别数组与普通对象: typeof([]) -> “object”
typeof(NaN) -> “number”
typeof(Symbol) -> “function”

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象 缺点:Number,Boolean,String基本数据类型不能判断

instanceof 判断对象的原型链上是否存在构造函数的原型。只能判断引用类型。
instanceof 常用来判断 A 是否为 B 的实例

Object.prototype.toString.call()
优点:精准判断数据类型 缺点:写法繁琐不容易记,推荐进行封装后使用

toString.call(()=>{}) // [object Function]
toString.call({}) // [object Object]
toString.call([]) // [object Array]
toString.call(‘’) // [object String]
toString.call(22) // [object Number]
toString.call(undefined) // [object undefined]
toString.call(null) // [object null]
toString.call(new Date) // [object Date]
toString.call(Math) // [object Math]
toString.call(window) // [object Window]

typeof为什么对null错误的显示
这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object

typeof(‘abc’)和 typeof 'abc’都是 string, 那么 typeof 是操作符还是函数?

解答:
typeof 的返回值之一为’function’,如果 typeof 为 function,那么 typeof(typeof) 会返回’function’,但是经测试,上述代码浏览器会抛出错误。因此可以证明 typeof 并非函数。
既然 typeof 不是函数,那 typeof 后面的括号的作用是?
括号的作用是进行分组而非函数的调用。—— 《javascript 高级程序设计》

// 举个例子
typeof (((func))); // is equal to typeof func

请解释下 NaN 表示什么呢?typeof NaN 结果是什么?(2020.01.12)
NaN:not a number,ECMA 规定其类型为"number",typeof NaN => “number”
判断一个只是否为 NaN?
// NaN是一个比较特殊的值
NaN === NaN; // false
// 由于NaN与任何值都不相等,那么如何判断一个值为NaN那
// 1. 字符串判断
number + “” === “NaN”;
// 2. 利用其与自身不相等
!test === test;
// 3. isNaN函数
isNaN(number);
// 4. Number.isNaN
Number.isNaN(number);
isNaN 与 Number.isNaN 区别:前者会发生隐式类型转换,后者只会在值为 NaN 时返回 true。

isNaN(“hello world!”); // true
Number.isNaN(“hello world!”); // false

isNaN会通过Number方法,试图将字符串"测试"转换成Number类型,但转换失败了,因为 Number(‘测试’) 的结果为NaN ,所以最后返回true。

而Number.isNaN方法,只是严格的判断传入的参数是否全等于NaN( ‘测试’ === NaN) ,字符串当然不全等于NaN啦,所以输出false。

js隐式类型转换(只需三分钟彻底掌握)

对象 == 字符串 —> 对象.toString()转换为字符串
null == undefined —> 相等,但是和其他值不相等
NaN == NaN —> 不相等
剩下的都是转换为数字进行比较。
代码演示
[] == ‘’ // 根据第一条规则
null == 0 // 根据第二条规则
[] == ![] // 根据运算符的优先级 -> [] == false -> 根据第四条规则 0 == 0 true

splice和slice你能说说有啥用和区别

slice
截取数组为主,也可以截取字符串
返回新的数组,包含截取的元素
不改变原数组

splice():数组增删查改
截取数组为主,不可以截取字符串
返回新数组包含得元素
改变原数组

JS中构造函数与普通函数的区别

1、调用方式不同
function fn1(){}
普通函数调用方式:直接调用fn1();
构造函数调用方式:需要使用new关键字来调用 let f = new fn1()

2、作用也不一样(构造函数用来新建实例对象)
3、首字母大小写习惯
一般构造函数的函数名称会用大写,普通函数用小写
4、函数中this的指向不同
普通函数中的this,在严格模式下指向undefined,非严格模式下指向window对象。
function foo(){
console.log(this===window);
}
foo();
//代码运行结果:true
构造函数的this则是指向它创建的对象实例。
function Foo(){
this.name = “令狐冲”;
}
var f = new Foo();
console.log(f.name);
//代码运行结果:令狐冲
//补充:构造函数的函数名和类名相同:Foo()这个构造函数,Foo是函数名,也是这个对象的类名。
5、写法的不同
function Person(name){
this.name = name;
}
var p = new Person(‘John’);//使用new关键字,不使用return

普通函数:
function person(name){
var obj = new Object();
obj.name = name;
return obj;//使用return
}
function person(name){
this.name = name;
return this;//使用return
}
var p = person(‘john’),

null 与 undefined 的区别

null表示一个"无"的对象,也就是该处不应该有值;而undefined表示未定义。
在转换为数字时结果不同,Number(null)为0,而undefined为NaN。
使用场景上
1.null:
作为函数的参数,表示该函数的参数不是对象
作为对象原型链的终点
2.undefined:
变量声明未赋值,等于undefined
调用函数时,未提供参数值,该参数等于undefined
对象没有赋值属性,该属性的值为undefined
函数没有返回值时,默认返回undefined

forEach与for循环,谁的性能更好?

for 循环没有任何额外的函数调用栈和上下文;
forEach函数前面实际上是
array.forEach(function(currentValue, index, arr), thisValue)
它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;

this指向

默认绑定: 非严格模式下 this 指向全局对象,严格模式下 this 会绑定为 undefined
隐式绑定: 满足 XXX.fn() 格式,fn 的 this 指向 XXX。如果存在链式调用, this 永远指向最后调用它的那个对象 隐式绑定丢失:起函数别名,通过别名运行;函数作为参数会造成隐式绑定丢失。
显式绑定: 通过 call/apply/bind 修改 this 指向
new绑定: 通过 new 来调用构造函数,会生成一个新对象,并且把这个新对象绑定为调用函数的 this 。
箭头函数绑定: 箭头函数没有 this ,它的 this 是通过作用域链查到外层作用域的 this ,且指向函数定义时的 this 而非执行时
优先级

原型与原型链
原型与原型链

事件循环机制
事件循环机制

1.padding margin 区别
内外边距
最大的区别是作用对象不同
padding 针对于自身
margin 作用于外部对象的

2.vw与%区别
% 有继承关系,继承父级的
vw 只和屏幕分辨率有关系

3.行内与块级元素
块级元素宽度继承父级
行内元素不换行,大小由内容决定,不支持宽高,不支持上下margin,不支持padding-top

4.谷歌支持小字体
transform:scale(0.8)

5.let var区别
var 1.声明提升
var没有局部作用域 let 有
var声明覆盖 let 会报错
const 声明得值一定要赋值,定义的值不能修改,支持块级作用域,一般常量大写

let a = 1;
let b = 2
通过es6的相关知识呼唤相关值
[a,b] = [b,a]

高频的函数操作可能产生不好的影响

比如 resize scoll mousedown mousemove keyup
这种在一瞬间对浏览器或服务器造成了过多的压力,就需要进行优化

一般就有两种解决方案

节流:高频触发时候,在一段时间内只执行一次,(概念: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。)
防抖:你尽管触发,在一段时间内我只触发最后一次。(概念: 规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。)

目的 就是降低一个函数的触发频率

js有那三部分组成
ECMAScript:js核心内容,描述了语言的基础语法 var for 数据类型 
文档对象模型(DOM)
浏览器对象模型(BOM)
js内置对象

String Boolean Number Array Object Fucntion Math date RegExp
常用的内置对象
Math abs() max()
Date new Date() getYear
Array
String concat() length() slice() split()

操作数组的方法有那些

push() pop() sort() splice() unshift() shift() reverse() concat()
join() map() filter() some() reduce() usArray() findIndex() find()

那些方法会改变原数组

ush() pop() unshift() shift() sort() reverse() splice()

js对数据类型的检测方式有那些

typeof() 对于基本数据类型有作用,引用数据类型无效
instanceof() 只能判断引用数据类型,不能判断基本数据类型
console.log([] instanceof Array); //tyue
console.log(‘abc’ instanceof String); //false

constructor()几乎可以判断基本和引用数据类型,如果声明了一个构造函数并把它的原型指向了Array这个时候就判断不出来了
a.constructor = Array
Object.prototype.toString.call()

闭包

什么是闭包?函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包
特点:可以重复利用变量,并且这个变量不会污染全局的一种机制,因为这个变量始终保存在内存当中
缺点:对内存消耗较大,页面性能下降,在IE浏览器中会导致内存泄漏 (手动删除,赋值为null)
使用场景:防抖 节流 函数嵌套函数避免全局污染。

前端内存泄漏怎么理解

js已经分配内存地址的对象,但是由于长时间没有释放,或者没办法清除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度慢,甚至崩溃情况。

垃圾回收机制
因素:一些未声明直接赋值的变量,一些未清空的定时器,过度的闭包,一些引用元素没有被清除。会导致内存泄漏

事件委托是什么

又叫事件代理,原理就是利用事件冒泡的机制来实现,也就是说把子元素的事件绑定到父元素身上,如果子元素阻止了事件冒泡那么委托也就不成立了
阻止事件冒泡:event.stopPropagation()
addeventListener(‘click’,函数名,true/false) 默认是false(事件冒泡),true 事件捕获
优点:提高性能,减少事件绑定,也就减少了内存的占用

基本数据类型和引用数据类型的区别

得看

说一下原型链

原型原型链
原型就是一个普通的对象,他是为了构造函数的实例共享属性和方法,所有实例中引用的原型都是同一个对象
使用prototype可以把方法挂在原型上,内存值保存一份,不用占多份
// 原型
function fn(){
this.say = function(){
console.log(‘hello’);
}
}
//内存中存一份
fn.prototype.look = function (){
console.log(‘look’);
}
//这样会占内存 会在内存中存多份
let f1 = new fn()
let f2 = new fn()

//一份
f1.look()
f2.look()

为什么proptotype方法可以找到:proto
proto:理解为是一个指针,是实例对象中的属性,是指向构造函数的原型prototype
console.log(f1.proto === fn.prototype); //true
一个实例对象在调用属性和方法的时候,会依次从实例本身,构造函数原型,到原型的原型上查找
实例对象与原型之间的链接叫原型链

有图
—>代表__proto__
Person --> person.prototype — Object --> Object.prototype --> null
p1 --> person.prototype — Object --> Object.prototype --> null

new操作符具体做了什么

1.创建一个空对象
2.将新对象和构造函数通过原型链进行链接
3.把构造函数的this绑定到新的空对象身上
4.执行构造函数中的代码
5.根据构造函数返回的类型判断,如果是值类型,则返回对象,如果引用类型,就返回引用类型

js如何实现继承的

1.原型链继承
2.借用构造函数继承
3.组合式继承
4.es6的class类继承

1.原型链继承

让一个构造函数的原型是另一个类的实例,那么这个构造函数new出来的实例就具有该属性的实例
function Parent(){
this.isShow = true;
this.info = {
name:‘abc’,
age:18
}
}
Parent.prototype.getInfo = function(){
console.log(this.isShow);
console.log(this.info);
}

function childs(){}
childs.prototype = new Parent()

let child1 = new childs()
child1.isShow = false
child1.info.play = 'pingpang'
child1.getInfo() //{name: "abc", age: 18, play: "pingpang"}

let pr1 = new Parent()
pr1.getInfo()
//有点写法简单容易理解
//缺点:对象实例共享所有属性和方法,无法向父类构造函数传参
2.借用构造函数继承

在子类的构造函数的内部调用父类的构造函数:使用apply() 或call()方法将父对象的构造函数绑定在子元素上
function Parent(play){
this.name = ‘ccc’
this.info = {
age:‘12’,
play
}
}

function Child(genter){
Parent.call(this,genter)
}

let child1 = new Child(‘男’)
child1.info.nickname = ‘xxx’
console.log(child1.info);

let child2 = new Child(‘女’)
child2.info.nickname = ‘xxx’
console.log(child2.info);
//优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题
//缺点:借用构造函数的缺点是方法都在构造函数中定义,因此无法实现函数复用。在父类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式

3.组合式继承

将原型链和借用构造函数的组合到一起,使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样即通过在原型上定义方法实现了函数复用,又能保证每个实例抖音自己的属性
function Person(genter){
console.log(‘执行次数’);
this.info = {
name:‘cccs’,
age:‘20’,
genter
}
}

Person.prototype.getInfo = function (){
console.log(this.info);
}

function Child(genter){
Person.call(this,genter)
}

Child.prototype = new Person()

let child1 = new Child(‘不知道’)
child1.info.ccname = ‘123434’
child1.getInfo()

let child2 = new Child('nvde me ')
child2.getInfo()

//优点:就是解决了原型链继承和借用构造函数继承造成的影响
//缺点:无论什么情况下会调用父类构造函数是两次,一次创建子类原型的时候,另一次在子类构造函数内部

4.es6的class类继承

通过extents关键字实现继承,其实质是先创造出父类的this对象,然后用子类的构造函数修改this
子类的构造方法中必须调用super方法,且只有调用了super()之后才能使用this,因为子类的this对象是继承父类的this对象,然后对其进行加工,而super方法表示的是父类的构造函数,用来新父类的this对象
class fn1{
constructor(kind){
this.kind = kind
}
getKing(){
return this.kind
}
}

// 继承
class fn2 extends fn1 {
constructor(name){
super(‘哈哈’)
this.name = name;
}
getthisInfo(){
console.log(this.name);
console.log(super.getKing());
}
}

let fn3 = new fn2(‘ooo’)
fn3.getthisInfo()
//优点:语法见到那
//缺点:并不是所有浏览器都支持class关键字

js的设计原理

js引擎 v8
运行上下文:浏览器里的api,不是js提供的。window,dom提供的。js运行的时候可以调用。
调用栈:单线程运行,js可做一些交互,如果是多线程会造成很多的同步问题,交互可能还会冲突。主线程主要是一些渲染工作,如果有有阻塞,就会导致卡死
事件循环:下面说
回调:就是加入到事件队列里面等待事件循环,只有调用栈为空的时候才会拿出来放入调用栈继续执行

this指向问题

1.全局对象中的,全局作用域,匿名函数,普通函数中的this:指向全局window
2.在不是箭头函数的情况下this永远指向最后调用他的那个对象
3.new 关键字改变了this的指向
4.apply call bind 可以改变this指向,不是箭头函数情况下
5.箭头函数中的this:指向在定义的时候就已经确定了,箭头函数它没有this,要看外层是否有函数,如果有就是外层函数的this。没有就是window

script 标签中async defer区别

为什么会有这两个属性defer或者async来辅助脚本加载那,因为浏览器在遇到script标签的时候,文档的解析会停止,不再构建document,有时打开一个网页上会出现空白一段时间,浏览器显示是刷新请求状态(也就是一直转圈),这就会给用户很不好的体验,defer和async的合理使用就可以避免这个情况,而且通常script的位置建议写在页面底部(移动端应用的比较多,这两个都是html5中的新属性)。

1.默认引用 script:
当浏览器遇到 script 标签时,文档的解析将停止,并立即下载并执行脚本,脚本执行完毕后将继续解析文档。

2.async模式
当浏览器遇到 script 标签时,文档的解析不会停止,其他线程将下载脚本,脚本下载完成后开始执行脚本,脚本执行的过程中文档将停止解析,直到脚本执行完毕。其它解释(并且执行顺序是无序的,谁先加载完谁先执行!有可能在DOMContentLoaded事件之前执行,也可能在DOMContentLoaded事件之后执行,但是一定在onload事件之前执行)

3.defer模式
当浏览器遇到 script 标签时,文档的解析不会停止,其他线程将下载脚本,待到文档解析完成,脚本才会执行。

所以async和defer的最主要的区别就是async是异步下载并立即执行,然后文档继续解析,defer是异步加载后解析文档,然后再执行脚本,这样说起来是不是理解了一点了呢?

结论:两者都是异步加载,不会阻塞dom元素的解析,并且严格按照书写顺序执行!
总体来说,defer和async的主要不同就是defer会保证脚本的顺序,async不保证顺序

当没有这里两个标签的时候,浏览器会立刻加载并执行指定的脚本
async:异步操作,加载和渲染后面元素的过程将和script的加载和执行并行进行
defer:异步操作,加载和渲染后面元素的过程将和script的加载和并行进行,但是他的执行时间要等所有元素解析完成之后才会执行
复杂情况下脚本很多的时候用到这个就行 可用到defer就很好,相当于把3,defer放到了底部
defer会保证脚本顺序 async 不会
注:defer 比async 早创造出来的时间节点早

另外补充一下:
何时触发DOMContentLoaded , onload 这两个事件?
1、当 onload 事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。
2、当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。

setTimeout最小执行时间

setTimeout 4ms
setInterval 10ms

ES6的新特性

1.块级作用 let const
不存在变量提示,存在暂时性死区的问题,块作用域内容,不能在同一个作用域内重复声明
2.定义类的语法糖 class
3.增加一个数据类型 symbol
4.解构赋值
从数组或者对象中取值,然后给变量赋值
5.函数参数的默认值
6.给数组新增了api forEach filter map reduce some()和every()方法
7.对象和数组都新增了扩展运算符
8.promise
解决异步编程的一种方案,回调地狱的问题,
自身方法有all reject resolve race方法
原型上有then catch
把异步操作队列话,按照预期的顺序返回结果
三种状态
pending初始 fulfiled成功 rejected失败
状态pending到fulfiled 或到 rejected一旦发生,状态就会凝固不会在变
async 和await
同步代码做异步的操作,两者必须搭配使用
async 表明函数内有异步操作调用函数会返回promise对象
await 是组成async的表达式,结果是取决于它等待的内容,如果是promise就是promise的结果 如果是普通函数就是链式调用
await 后的promise 如果是reject状态那么整个函数都会中断后面的代码执行

9.新增模块化import export
10.set map数据解构
set 不重复 唯一性
map key类型不受限制

11.generator
12.箭头函数
箭头函数普通函数区别
箭头
不能作为构造函数使用,不能用new
箭头函数没有原型
没有arguments
不能用call aplay bind 改变this指向
this指向外层第一个函数的this

call apply bind 区别

call apply 功能类似传参方法不同
call 方法是一个参数列表
apply 是一个数组
bind 不会立刻执行会返回一个改变this指向的函数,这个函数还是可以传参的,bind()()
call方法性能比apply好一些,用的更多一些

递归遇到的问题

如果一个函数内可以调用函数本身就是递归函数(函数内部调用自己)
特别注意:必须要有退出条件

10/23

如何实现一个深拷贝

深拷贝就是完全拷贝一份新的对象,会在堆内存中开辟新的空间,拷贝对象被修改后,原对象不受影响。主要针对的是引用数据类型
let obj = {
a:1,
b:[1,2,3,4],
c:null,
d:{
kk:123
},
ff:function(){
console.log(1);
}
}
1.扩展运算符
let obj1 = {…obj}
缺点只能针对第一层深拷贝,多层不能。

2.JSON.parse(JSON.stringfy())
缺点:不能拷贝方法

3.利用递归函数实现

function copy(obj) {
    let newObj = null; // 声明一个对象来存储拷贝之后的内容
    // 判断数据类型是否是复杂的数据类型,如果是则调用自己,如果不是则直接赋值即可!
    // 由于null不可以循环但是他的类型又是object,所以这个需要对null进行判断
    if (typeof(obj) == 'object' && obj !== null){
		// 声明一个变量用以存储拷贝出来的值,根据参数的具体数据类型声明不同的类型来存储
		newObj = obj instanceof Array? [] : {};
		// 循环obj的每一项,如果里面还有复杂的数据类型的话,则直接利用递归函数再次调用。
		for(let i in obj){
			newObj[i] = copy(obj[i])
		}
    } else {
        newObj = obj
    }
    return newObj; // 函数没有返回的值的话,则为undefined
}
说一下事件循环

看博客

ajax是什么

可以在不重新加载网页的情况下更新数据,对页面部分内容更新
通过XmlHttpRequest对象像服务器发送异步请求,然后从服务器拿到数据,最后通过js操作DOM形式更新页面
过程:
1.创建XmlHttpRequest对象
2.通过XmlHttpRequest对象里的open方法和服务器建立连接
3.构建请求所需数据,并通过XmlHttpRequest对象send方法发送给服务器
4.通过XmlHttpRequest对象的onreadystate change 事件来监听服务器和你的通信状态
5.接受并处理服务器响应的数据结构。
6.处理的数据更新到HTML上

get post 区别

1.post请求更安全(不会作为url的一部分,不会被缓存、保存在服务器日志、以及浏览器浏览记录中,get请求的是静态资源,则会缓存,如果是数据,则不会缓存)
2.post请求发送的数据更大(get请求有url长度限制,http协议本身不限制,请求长度限制是由浏览器和web服务器决定和设置)
3.post请求能发送更多的数据类型(get请求只能发送ASCII字符)
4.传参方式不同(get请求参数通过url传递,post请求放在request body中传递)
5.get请求的是静态资源,则会缓存,如果是数据,则不会缓存
6.get请求产生一个TCP数据包;post请求产生两个TCP数据包(get请求,浏览器会把http header和data一并发送出去,服务器响应200返回数据;post请求,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 返回数据)

post请求的过程:
(1)浏览器请求tcp连接(第一次握手)
(2)服务器答应进行tcp连接(第二次握手)
(3)浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)
(4)服务器返回100 Continue响应
(5)浏览器发送数据
(6)服务器返回200 OK响应

get请求的过程:
(1)浏览器请求tcp连接(第一次握手)
(2)服务器答应进行tcp连接(第二次握手)
(3)浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)
(4)服务器返回200 OK响应

Promise底层原理优缺点

Promise对象,封装了一个异步操作并且还可以获取成功或失败的结果,解决低于回调的问题。之前如果异步任务比较多,同时他们之间又有相互依赖关系,只能用回到函数处理。这样就容易形成回调地狱,代码可读性差,可维护性也很差。
Promise有三种状态:pending fulfild rejected
状态改变只会有两种情况 pending到fulfiled 或到 rejected一旦发生,状态就会凝固不会在变

缺点:无法取消Promise一旦创建它就会立即执行,不能中途取消,如果不设置回调promise内部抛出的错误就无法反馈到外面,若当前处于pendding状态时无法得知目前在那个阶段
原理:
构造函数Peromise实列,实例需要传递函数参数,这个函数有两个形参,分别都是函数类型resolve和reject
promise身上还有then方法,这个方法就是来指定状态改变时的确定操作resolve执行第一个函数,reject执行第二个函数

Promise和async await区别

1.都是处理异步的请求方式
2.promise ES6语法,async await 是ES7
3.async await基于Promise实现的,它和promise都是非阻塞性的
优缺点
1.promise是返回对象我们要用then,catch方法处理和捕获异常,并且书写方式是链式调用,容易造成代码重叠不好维护。async await 是同过try catch 进行捕获异常
2.async await能够让代码看起来像同步一样,只要遇到await就会立刻返回结果,然后在执行后面的操作。promise只能通过then方式返回结果会出现请求还没返回就执行了后面操作

浏览器的存储方式有那些

1.cookie
h5标准前的本地存储方式,兼容好,请求头自带cookie,存储量太小,资源浪费,使用麻烦,要使用还要进行封装
2.localstorage
h5加入的以键值对为标准的方式操作方便永久存储兼容性好
缺点:保存值类型被限定,浏览器在隐私模式下不可读取,不能被爬虫。
3.sessionstorage
当前页面关闭后就会立刻清除,只是绘画级别的存储方式
4.indexedDB
h5标准的存储方式他是以键值对进行存储,可以快熟读取,适合web场景

token 存在sessionstroage 还是localsession

token:验证身份令牌,一般是用户通过账号密码登录后服务端把这些凭证通过加密等一系列操作后得到的字符串。
一般存local
1.localstroage 里后期每次请求接口都需要把他当作一个字段传给后台。
2.存cookie中是会自动发送,缺点就是不能跨域
如果存在localstorage中,容被xss攻击,但是如果做好对应措施,那么是利大于弊的
如果存在cookie中会有CSRF攻击

token登录流程

1.客户端用账号密码请求登录
2.服务端验证账号密码
3.验证成功之后,服务端会签发一个token,把这个token发送给客户端
4.客户端收到后保存起来可以放在cookie也可以是localstorage
5.客户端每次向服务端请求资源的适合都需要携带这个token
6.服务端收到请求没接着去验证客户端里的token,验证成功会返回客户端请求数据

页面渲染过程

DNS解析
建立tcp连接
发送http请求
服务器处理请求
渲染页面
####### 可看博客
断开tcp连接

DOM树和渲染树区别
DOM树是和HTML标签一一对应,包括head和隐藏元素
渲染树不包含head和隐藏元素
svg格式了解多少

基于xml语法格式的图像格式,可缩放矢量图,(其它是基于像素的),svg是属于对图像形状的描述,本质是文本文件,体积小并且不管放大多少倍都不会失真。
1.svg可直接插入到页面中,成为DOM的一部分,然后用js或css进行操作(
2.可以作为文件直接引用
3.svg可以转为base64引入页面

npm 底层环境是什么

node package manager node的包管理和分发工具是js的运行环境
npm组成:网站,注册表,命令行工具

http协议规定的协议头和请求头是什么

1.请求头信息
Accept:浏览器告诉服务器支持的数据类型
host:浏览器告诉服务器访问那台主机
Referer:浏览器告诉服务器那个页面过了的,一般是做防盗链的
User-Agent:浏览器类型,版本信息
Date:告诉我是什么时候访问的
Connection:连接方式
cookie
X-request-with:请求方式
2.响应头信息
Location:这个就是告诉浏览器你要去找谁
Server:告诉浏览器服务器类型
Content-Type:告诉浏览器返回的数据类型
Refresh:控制浏览器定时刷新

浏览器的缓存策略

强缓存(本地缓存),协商缓存(弱缓存)
强缓:不发起请求直接使用缓存里的内容,浏览器把js,css,image等存到内存中,下次用户访问直接从内存中取提高性能
协缓:需要向后台发请求通过判断来决定是否使用协商缓存,如果请求内容没有改变则返回304,浏览器就用缓存里的内容。

说一下同源策略

http://www.aaa.com:8080/index/vue.js
http 协议
www 子域名
aaa 主域名
8080 端口号
/index/vue.js 资源
同源策略是浏览器的核心,如果没有这个策略就会遭受网络攻击,主要指的是协议域名端口号三者一致,若其中一个不一样则不是同源会产生跨域
三个允许跨域加载资源的标签 img link script
跨域是跨域发送请求,后端也会正常返回结果,只不过这个结果被浏览器拦截了
JSONP 利用script解决跨域的(也是服务器支持才可以)
CORS 后端实现就解决了
websocket 双向数据通信
反向代理 中转站类似

防抖和节流是什么 从新看

都是应对高频触发事件的优化方案
防抖:避免事件重复触发
使用场景:1.频繁和服务端交互,2.输入框自动保存事件
节流:一段时间内只执行一次
使用场景 scroll resize

什么是json

是一种纯字符串的数据,适合网络中传输,可存在cookie localStroage 中
使用:JSON.parse JSON.strningfy()
什么适合使用:定义接口,序列化,配置文件

无感登录 无感刷新token

响应器中拦截,判断返回过期后调用刷新接口
后端给过期时间,前端判断,然后调用刷新接口
定时器刷新浪费资源
流程:登录保存token,2.拦截401 后刷新token,3.替换token 本地和对象里的,4再次发送未完成请求,如果新token也过期了 就退出

大文件上传怎么做

分片上传:
分为小块上传,每个数据块要有标识,按照规则发送各个数据块,服务端整合成原始文件
断点续传:
服务端返回从哪里开始 浏览器自己处理

语义化的理解

再写html页面结构时候,所用标签有意义
怎么判断有语义化了没有,把css去掉显示内容较为正常

h5c3新特性

H5:语义化标签,音视频 画布 数据存储local session 表单控件 email url search ,拖拽释放
C3:
新增选择器;属性,伪类,元素选择器
增加了媒体查询
文字阴影
盒子模型 box-sizing
渐变
过度
自定义动画
背景属性
2D,3D

rem如何做适配的

相对根元素适配的 html 的font-size 属性计算大小。
rem是根据根元素font-size计算值的倍数
比如html上的font-size:16px 给div设置1.5rem div实际像素就是16px* 1.2 = 19.2px

解决了那些移动端兼容问题

1.设置样式overflow:scroll/auto时,ios上的滑动会卡顿
解决:-webkit-overflow-scrolling:touch
2.安卓环境下placeholder文字设置行高会偏上
解决:input有placeholder属性时候不设置行高
3.移动端字体小于12px时异常显示
解决:整体放大一倍,然后transform进行缩小
4.ios下input按钮设置了 disabled属性为true显示异常
解决input[type=button]{opcity:1}
5.安卓手机取消语音输入按钮
input::-webkit-input-speen-button{diaplay:none}
6.IOS取消input输入框在输入英文首字母默认大写

7.禁用ios 安卓用户选中文字
添加全局样式 -webkit-user-select:none

v-if v-show

都是控制元素显示隐藏
1.v-show控制元素的display值来让元素显示隐藏的,v-if显示是把DOM元素整个添加和删除
2.v-if有局部编译/卸载的过程,切换这个过程中,会适当的销毁和重建内部的事件监听和子组件。v-show只是简单的css切换
3.v-if是真正的条件渲染 v-show 从false变成true不会触发组件的生命周期v-if会触发生命周期
4.v-if的切换效率比较低v-show高

如何理解MVVM

链接

v-for 中key的作用

key属性是dom元素的唯一标识
作用:
1.提高虚拟dom的更新
2.若不设置key可能会触发一些bug(比如有个列表更新,有一行更新了,可能别的行也更行了)
3.为了触发过度效果
链接

vue的生命周期

组件从创建到销毁的过程
创建
beforeCreate
在这个阶段属性和方法都不能使用
created
实例创建完成之后,完成了数据监测,可以使用数据,修改数据,不会触发updated,不会更新视图
挂载
beforeMount
完成了模板的编译,虚拟dom也完成创建,即将渲染,修改数据,不会触发updated
Mounted
把编译好的模板挂载到页面,这里可以发送异步请求也可以访问dom节点
更新
beforeUpdate
组件数据更新之前使用,数据是新的页面上数据是旧的,组件即将更新,准备渲染,可以改数据
updated
render函数从新做了渲染,数据和页面都是新的,避免这里更新数据,可能无线循环
销毁
beforeDestory
实例销毁前,在这里实例还可以用,可以清除定时器事件等
destroyed
组件已经销毁了,事件,监听,子组件等全部销毁
使用keep-alive
activited
组件激活时
deactivited
组件销毁时

created mounted去请求数据有什么区别

created 模板渲染html之前调用,通常先初始化属性,然后做渲染
mounted 模板渲染完成之后调用,一般都是初始化页面后再对元素节点进行操作。在这里请求数据可能会出现闪屏问题
一般用create 比较多
请求的数据对dom有影响,那么使用created
如果请求的树对dom无关,那么使用mounted

vue中修饰符有那些

1.事件修饰符 .stop阻止冒泡 .prevent阻止默认行为 .capture内部元素触发的事件现在此次处理 .self只有在event.target是当前元素时触发 .once只触发一次 .passive立即触发默认行为 .native把当前元素作为原生标签看待
2.按键修饰符 .keyup .keydown
3.系统修饰符 .ctrl .alt .meta
4.鼠标修饰符 .left .right .middle
5.表单修饰符 .lazy .trim .number

element-ui是怎么做表单验证的

1.在表单中加rules属性,然后data中写校验规则
2.内部添加规则
3.自定义函数校验

vue组件之间通信

父传子
props
r e f 引用信息会注册在父组件的 ref 引用信息会注册在父组件的 ref引用信息会注册在父组件的refs上
子传父
$emit 绑定自定义事件触发执行后传给父组件,父组件要用事件监听来接受参数
兄弟通信 new 一个新的vue实例,用on和emit来对数据进行传输
vuex传值

keep-alive是什么?怎么使用?

vue的一个内置组件,包裹组件的时候会缓存不活跃的组件实例,并不是销毁他们
作用:把组件切换的状态存在内存里,防止重复渲染DOM节点,减少加载事件和性能消耗,提高用户体验
include 和exclude
router 里缓存可以添加meta:{keepAlive:false/true}

vue路由是怎么传参的

1.parmas
this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …em.a}) 获取:this.route.params.id
2.路由属性传参
this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …({name:'/index/{item.id}‘})
路由配置 {path:’/index:id’}
3.query传参(可以解决页面刷新参数丢失的问题)
this.$router.push({name:‘index’,query:{id:item.id}})

vue路由hash模式和history模式有什么区别

1.hash的路由地址上有#
2.在做回车刷新的时候hash模式会夹杂对应页面,history会报错404
3.hash模式支持低版本浏览器,his不支持
4.hash不会重新加载页面,单页面应用必备
5.history有历史记录,h5新增了pushstate和replaceState 去修改历史记录,并不会立刻发送请求
6.history是需要后台配置

路由拦截是怎么实现的

需要在路由配置中添加一个字段,它是用于判断路由是否需要拦截的
{
name:‘index’,
path:‘/index’,
component:Index,
meta:{
requirtAuth:true
}
}
router.beforeEach((to,from,next)=>{
if(to.meta.requirtAuth){
//拦截或者做一些操作
if(token){
next()
}else{
跳转
}
}
})

说一下动态路由

路由配置中设置meta属性,扩展权限相关的字段,在路由导航守卫里通过判断这个权限标识,实现路由动态增加和跳转
1.用户登录角色
2.根据角色判断,根据路由表的meta.role进行匹配把匹配到的路由形成可访问的路由

如何解决刷新后二次加载路由

1.window.location.reload()
2.metcher
新老路由比对
const router = createRouter()
export function reseRouter(){
const newRouter = creatRouter()
router.matcher = newRouter.matcher
}

computed watch区别

1.computed是计算属性,watch是监听,监听data中数据的变化
2.computed支持缓存的,依赖的属性值发生变化,计算属性才会从新计算,否则用缓存。watch是不支持缓存的
3.computed不支持异步,watch是可以异步操作的
4.computed是第一次加载就监听,watch是不监听
5.computed函数中必须有return watch不用

vuex在什么场景回去使用属性有那些

state getters mutation actions modules
使用场景:用户个人信息,购物车,订单 数据大的地方都可以

vue的双向数据绑定原理

通过劫持和发布订阅者模式来实现的,同时利用object.defineProperty()这个方法来劫持各个属性得getter和setter在数据发生的时候发布消息给订阅者触发对应的监听回调来渲染视图,也就是说数据和视图是同步的,数据改变视图改变视图改数据改。
实现步骤
1.需要obsever的数据对象进行递归遍历包括子属性对象的属性都加上setter和getter
2.compile模板解析指令,把模板中的变量替换成数据,然后初始化渲染视图,同时每个指令对应的节点绑定上更新函数,添加订阅者,如果数据变化收到通知,更新视图
3.watcher订阅者是Obsever和Compile之间的通信桥梁
作用:
1.在自身实例话的时候往订阅器内添加自己
2.自身要有一个update()方法
3.等待属性变动时调用自身的update方法,触发compile中的回调
4.MVVM作为数据绑定的入口整合了Obsever,compile和watcher三者是通过Obsever来监听自己的数据变化,从过compile解析模板指令,最后利用watcher把Obsever和compile联系起来,最终达到数据更新视图更新,视图更新数据更新的效果

了解diff算法和虚拟dom

虚拟dom,描述元素和元素之间的关系,创建一个js对象。如果组件内有响应的数据,数据发生改变的时候render函数会生成一个新的虚拟dom这个新的虚拟dom会和旧的虚拟dom
进行比对,找到需要修改的虚拟dom内容,然后去对应的真实dom中修改。
diff算法就是虚拟dom的比对时用的,返回一个patch对象,这个对象的作用就是存储两个节点不同的地方,最后用patch里记录的信息进行更新真实dom
算法步骤:
1.js对象表示真实的dom结构,要生成一个虚拟dom,再用虚拟dom构建一个真实dom树,渲染到页面
2.状态改变生成新的虚拟dom,跟旧的虚拟dom进行比对这个比对的过程就是diff算法,利用patch记录差异
3.把记录的差异用在第一个虚拟dom生成的真实dom上,视图就更新了

vue和jquery区别

1.原理不同 vue数据绑定 jq是先获取dom在处理
2.着重点不同 vue数据 jq操作

vuex响应式处理

vuex响应式处理

如何封装一个组件

1.vue.extend 创建
2.vue.component 注册
3.如果有子组件需求可以在props中接收定义
4.子组件修改数据,要把数据传递给父组件可以用emit
原则:
把功能拆开
尽量组件原子话,一个组件做一件事情
容器组件管数据,展示组件管视图

封装一个可复用组件需要满足什么条件

1.低耦合,组件之间依赖越小越好
2.最好从父级传入信息,不要公共组件请求数据
3.传入数据要校验
4.处理事件的方法写在父组件中

过滤器该怎么使用

vue的特性,用来对文本进行格式化处理,使用它的两个地方一个是差值表达式,一个是v-bind
分类:
1.全局过滤器
Vue.filter(‘abd’,function(v){
return v < 10 ? ‘0’ + v :v
})

{{33 | add}}

2.本地过滤器
和methods同级
filter:{
add:function(v){
return v < 10 ? ‘0’ + v :v
}
}
{{33 | add}}

vue如何强制刷新

组件切换时候有的页面没有改变需要刷新
1.location.reload
2.this. r o u t e r . g o ( 0 ) 3. p r o v i d e 和 i n j e c t 4. t h i s . router.go(0) 3.provide 和inject 4.this. router.go(0)3.provideinject4.this.forceUpdate()

vue2与vue3 区别

1.双向数据绑定原理不同 vue2数据劫持加发布订阅模式实现的,vue3 proxy数据代理,相比与2它能偶监听所有对象省去循环可以监听数组
2. 是否支持碎片 vue2 不支持 3支持 (就是多个根节点)
3. API不同 vue2选项内api,vue3组合api
4. 定义数据变量方法不同vue2 data中数据方法在methods。vue3 stepup
5. 声明周期不同
6. 传值不同
7. 指令和插槽不同
8.main.js不同

vue的性能优化

1.编码优化
不要把所有数据都放在data中(因为会增加setter,geetter,会收集对应wathcer)
v-for时给每个元素绑定事件用事件代理
keep-alive缓存组件
尽可能拆分组件,提高复用性,维护性
key值要保重唯一
合理使用路由懒加载,异步组件
数据持久化存储的时候尽量使用防抖节流优化
2.加载优化
按需加载
懒加载
图片懒加载
3.用户体验
股价屏
4.SEO优化
预渲染
服务端优化ssr
5.打包优化
cdn形式加载第三方模块
多线程打包
抽离公共文件
6.缓存和压缩
客户端缓存,服务端缓存
服务端进行Gzip压缩

nextTick 是什么?

答:获取更新后dom内容

created(){
    console.log(1);
    this.$nextTick(()=>{
    console.log(3);
    })
},
mounted(){
    console.log(2);
    this.$nextTick(()=>{
    console.log(4);
    })
},

输出 1,2,3,4 因为 nextTick 是异步的
应用场景

<button @click="btn" ref="bb">{{str}}</button>
data(){
    return {
    str:'123'

    }
},
methods:{
    btn(){
    // this.$refs.bb.innerHTML = 'bbb'
    this.str = 'bbbb'
    console.log(this.$refs.bb.innerHTML); //获取的还是123 不是更新后的结果
    this.$nextTick(()=>{
        console.log(this.$refs.bb.innerHTML);//bbbb
    })
    }
},

插件延迟计算了我们的宽高,所以要获取到更新后的dom

computed vs methods
computed 有缓存的,值没改变就一直使用之前缓存内容
methods 没有缓存 调用一次执行一次

两者都可以返回东西,但computed 性能好,但在特定的情况下还是要用methods 比如跳转

Vue让css只在当前页面生效

  1. props 和data优先级谁高?
    props ==> methods ==> data ==> computed ==> watch

  2. vuex 有哪些属性?
    state 类似于组件中的data,存放数据的。
    getters 类似于computed
    mustations 类似于组件中的methods
    action 提交mutations的
    modules 把以上四个属性再细分,让仓库更好管理

vuex中mutations和aciton 区别?

相同:
都可以写方法,但是约定俗成的是不会把方法写在action 都写在mutations

区别:
mutations:同步的事务
action:可以包含异步的操作

参数有区别
action 作用提交的是mutation,而不是直接变更状态
action 可以包含任意异步操作
以上区别在调试中可以看出来

action: fn({commit,state})
mutations: fn(state)

modules
比如项目庞大数据还是不好管理,state数据有50个或者更多用到modules再细分 方便管理

面试官想听到的自我介绍
我叫张三,最近几年一直在从事前端工作,我会什么技术,直接聊技术

vuex 是单向数据流还是双向数据流?

单项数据流,不能改,即使v-module双向绑定也不能改,只能mutations改

vuex如何做持久化存储?

vuex本身不是持久化数据,它是集中存储的
持久存储方法?
使用localstorage自己写
使用插件(vuex持久化插件)

首屏优化应该怎么做

1.使用路由懒加载 (直接加载可能会把所有路由加载)
2.非首屏组件使用异步组件 不然可能会加载所有组件
3.首屏不重要的组件延迟加载
4.静态资源放在cdn
5.减少首屏js,css等资源文件的大小
6.使用服务端渲染
7.减少DOM数量和层级
8.精灵图
9.做loding
10.开启Gzip压缩
11.图片懒加载

vue3性能为什么必vue2好

1.diff算法的优化 vue2是全量比较 vue3根据内容添加静态标记,之后再和上次虚拟节点比较值比较静态标记点
2.静态提升 vue2 不管有没有更新每一次都要去创建,然后渲染。vue3使用静态提升后对没有参与的更新的元素只会创建一次
3.事件侦听缓存 vue3同一个函数复用事件提升性能

vue3为什么使用proxy

1.proxy可以代理整个对象,defineProperty只是代理对象上的某个属性
2.proxy代理对象的监听更丰富
3.proxy代理对象会生成新的对象不会修改被代理对象(defineProperty会直接修改)
4.proxy不兼容ie浏览器

说一下对组件的理解

可以重复使用的vue实例,独一无二的组件名称,可以抽离单独的公共模块。比如 头部 底部 上传图片 提高代码复用率

如何规划项目文件

pubilc:
图表,index.html img
src
api(可以封装一些请求的文件)
assets 主题样式静态资源 iconfont文件一些js
components
按分类再次划分子目录
plugins第三方插件
router
static 静态资源
styls
utils 公共方法
views
App.vue
main.js
package.json

seo如何优化

1.ssr 服务端渲染
2.预渲染 prerendr-spa-plugin

webpack打包和不打包的区别

1.运行效率
2.对技术的支持不够,如果写一些高级的语法es6,es7有的浏览器是不支持的,webpack可以编译处理转成浏览器可以识别的文件

webpack是怎么打包的,babel是做什么的

1.会把js css 图片这些文件视为一个文件用impore或者require引入
2.找到入口文件,通过入口文件找到关联的依赖文件把他们打包到一起,把bundle文件拆分成多个小的文件,然后异步按需加载所需要的文件,如果一个被多个文件引用打包时只会生成一个文件 (方法或文件变量如果没使用不会被打包)。
对于多个入口文件,加入引入了相同的代码,可以用插件把他抽离到公共文件中
bable高级语法转成低级语法

git如何合并抽取代码

拉取 git pull ‘第一次要输入仓库地址’
上传前查看状态 git sattus (会显示修改文件,master)
提交到本地缓存区 git add .
提交到本地仓库 git commit -m ‘修改描述’
提交到远程仓库 git push ‘仓库地址’ master 以后就push
创建分支 git branch -b xxx
合并分支 git merge ‘合并分支的名字’ 合并后继续push

git如何解决冲突问题

首先一定要沟通,找到打架修改原因
1.两个分支中修改了同一个文件
2.两个分支中修改了同于1个文件的名字
解决
1.当前分支上,直接修改代码 然后add commit
2.在本地当前分支上修改冲突代码 add commit push

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值