说一说cookie、sessionStorage、localStorage 的区别?
共同点
Cookie、SessionStorage、 LocalStorage都是存储在浏览器本地
区别
写入方式
- Cookie 由服务端写入
- SessionStorage、 LocalStorage都是由前端写入
生命周期
- Cookie 的生命周期是由服务器端在写入的时候就设置好的
- LocalStorage是写入就一直存在,除非手动清除
- SessionStorage 是页面关闭的时候就会自动清除
存储大小
- cookie的存储空间比较小(大概4KB)
- SessionStorage、 LocalStorage存储空间比较大(大概5M)
请求是否携带
- 在前端给后端发送请求的时候会自动携带Cookie中的数据
- SessionStorage、 LocalStorage中存储的数据不会自动携带到服务端
应用场景
- Cookie一般用于存储登录验证信息(token或sessionId)
- LocalStorage常用于存储不易变动的数据,减轻服务器的压力
- SessionStorage可以用来检测用户是否是刷新进入页面,如音乐播放器恢复播放进度条的功能
说一说JS数据类型有哪些,区别是什么?
js数据类型分为两大类,一类是基本数据类型,也称简单数据类型,分别是Number、String、Boolean、Undifined、Symbol、BigInt、Null,另一类是引用数据类型也叫复杂数据类型,通常用Object表示,普通对象,数组,正则,日期,Math数学函数都属于Object。
区别
- 基本数据类型直接存储在栈内存中,占据空间小,属于被频繁使用的数据
- 引用数据类型是存储在堆内存中,占据空间大,引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体
说一说JavaScript有几种方法判断变量的类型?
JavaScript共有4种判断变量类型的方法,分别是typeof、instanceof、Object.prototype.toStrig.call()(对象原型链判断方法)、 constructor(用于引用数据类型) 。
typeof
常用于基本数据类型,对于引用数据类型除了function返回的是"function",而其他引用数据类型全部返回的是"object"。
注意:typeof无法判断null和其他除了Function以外的引用数据类型,因为都返回的是“object”
typeof "abc"; // "string"
typeof 123; // "number"
typeof undefined; // "undefined"
typeof true; // "boolean"
typeof Symbol('symbol'); // "symbol"
typeof 12434n; // "bigint"
typeof null; // "object"
instanceof
主要用于区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类型中的undefined, null, symbol检测不出来。
fuction test(){}
test instanceof Function; // true
test instanceof Object; // true
上面的代码可以看出,使用instanceof可以检测出test的类型,但是还是不是最准确的,如果直接使用test instanceof Object只能判断出改变量是 Object,却不能判断是不是Function类型。
constructor
用于检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。
function test() {}
test.constructor === Function; true
test.constructor === Object; false
Object.prototype.toString.call()
适用于所有类型的判断检测,检测方法是Object.prototype.toString.call(数据) 返回的是该数据类型的字符串。 这四种判断数据类型的方法中,各种数据类型都能检测且检测精准的就是Object.prototype.toString.call()这种方法。
加分回答
instanceof的实现原理:验证当前类的原型prototype是否会出现在实例的原型链__proto__上,只要在它的原型链上,则结果都为true。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,找到返回true,未找到返回false。 Object.prototype.toString.call()原理:Object.prototype.toString 表示一个返回对象类型的字符串,call()方法可以改变this的指向,那么把Object.prototype.toString()方法指向不同的数据类型上面,返回不同的结果
说一说JS实现异步的方法?
所有的异步任务都是等同步任务执行完之后,从任务队列中依次取出执行。JavaScript中的异步方法主要有 回调函数、定时器(setTimeout、setInternal)、Promise、生成器Generators/yield、 async/await。
回调函数
回调函数是异步操作的最基本的方法,比如Ajax回调。
优点
- 简单、容易理解和实现
缺点
- 不利于代码的维护和阅读,各个部分之间高度耦合。
- 每个任务只能指定一个回调函数
- 不能使用try/catct捕获异常
Promise
Promise不仅能捕获异常,而且还能很好的解决回调地狱的问题。
优点
- Promise不仅能捕获异常,而且还能很好的解决回调地狱的问题
缺点
- 无法取消Promise
- 错误需要通过回调函数捕获
Generator
Generator 函数是 ES6 提供的一种异步编程解决方案,Generator 函数是一个状态机,封装了多个内部状态,可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。
优点
- 异步语义清晰
缺点
- 手动迭代
Generator函数很麻烦 - 实现逻辑有点绕
async/await
async/await使得异步代码看起来像同步代码
优点
- 使用方法清晰明了
- 异步语义清晰
缺点
- await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低
加分回答
JS 异步编程进化史:callback -> promise -> generator/yield -> async/await。 async/await 函数对 Generator 函数的改进,体现在以下三点:
- 内置执行器。 Generator 函数的执行必须靠执行器,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
- 更广的适用性。 yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
- 更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。 目前使用很广泛的就是promise和async/await
说一说你对闭包的理解?
概念
一个函数和词法环境的引用捆绑在一起,这样的组合就是闭包。一般就是一个函数A,return其内部的函数B,被return出去的B函数能够在外部访问A函数内部的变量,这时候就形成了一个B函数的变量背包,A函数执行结束后这个变量背包也不会被销毁,并且这个变量背包在A函数外部只能通过B函数访问。
闭包形成的原理
- 作用域链,当前作用域可以访问上级作用域中的变量
闭包解决的问题
- 能够让函数作用域中的变量在函数执行结束之后不被销毁,同时也能在函数外部可以访问函数内部的局部变量。
闭包带来的问题
- 由于垃圾回收器不会将闭包中变量销毁,于是就造成了内存泄露,内存泄露积累多了就容易导致内存溢出
闭包的应用
- 能够模仿块级作用域
- 能够实现柯里化
- 防抖函数和节流函数
- Vue中数据响应式Observer中使用闭包等
说一说promise是什么与使用方法?
概念
Promise 是异步编程的一种解决方案,解决了回调地狱的问题,让代码的可读性更高,更容易维护。
Promise使用
- Promise是ES6提供的一个构造方法,可以通过
new Promise生成一个Promise对象。Promise构造函数接收一个函数作为参数,这个函数有两个参数,分别是两个函数resolve和reject。 resolve将Promise的状态由等待变为成功,将异步操作的结果作为参数传递过去reject则将状态由等待转变为失败,在异步操作失败时调用,将异步操作报出的错误作为参数传递过去- 实例创建完成后,可以使用
then方法分别指定成功或失败的回调函数,也可以使用catch捕获失败,then和catch最终返回的也是一个Promise,所以可以链式调用
Promise的特点
- Promise对象的状态不受外界影响(Promise对象代表一个异步操作,有三种状态),pending(执行中)、resolved(成功,又称fulfilled)、rejected(拒绝)。其中pending为初始状态,Promise始终都会从pending状态转为resolved(成功)或从pending状态转为rejected)(拒绝),这这个过程中不会受到其他因素影响。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
resolve方法的参数是then中回调函数的参数,reject方法中的参数是catch中的参数then方法和catch方法 只要不报错,返回的都是一个fullfilled状态的promise
Promise的其他方法
Promise.resolve()返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法Promise.reject()返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法Promise.all()返回一个新的promise对象,该promise对象在参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败Promise.any()接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值Promise.race()当参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象
说一说跨域是什么?如何解决跨域问题?
概念
跨域就是违反了 浏览器 的同源策略造成的,当前页面中的某个接口请求的地址和当前页面的地址如果协议、域名、端口其中有一项不同,就说该接口跨域了
同源策略
端口、协议、域名相同
浏览器为什么要限制跨域?
为了保证网页的安全
如何解决跨域问题?
CORS 跨域资源共享
-
实现方式
CORS实现跨域的关键是服务器,只要服务器实现了CORS接口,就可以实现跨域通信;服务器对于CORS的支持主要是通过给请求头header设置
Access-Control-Allow-Origin属性,这个属性的作用是允许哪一个请求源请求。如果Access-Control-Allow-Origin设置了*,其含义就是允许所有的请求源访问。res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader("Access-Control-Allow-Methods", "GET, PUT, OPTIONS, POST"); -
存在的问题
- 只能设置一个源;
- 如果Access-Control-Allow-Origin设置了* ,会不安全
JSONP回调函数方式,需要服务端支持
-
实现方式
JSONP主要是通过带有是scr属性的的标签来实现的跨域,比如script、img、link、iframe等等。具体的实现方式是把我们要请求的服务器(接口)地址放在scr属性的值上面,并且在请求地址后面拼接一个参数,这个参数的值是我们在本地定义的一个方法。当服务器拿到这个参后把客户端所要的数据传入这个参数对应的回调方法内,其实相当于执行了这个回调方法。这时候客户端就可以在这个方法内处理逻辑了
<script src="http://127.0.0.1:4000/list?callback=getData"> function getData(res) { console.log(`${res}就是服务给我们返回的数据了`); } </script> -
存在的问题
- JSONP只能处理GET请求;
- 不安全
node中间件、nginx反向代理
跨域限制的时候浏览器不能跨域访问服务器,node中间件和nginx反向代理,都是让请求发给代理服务器,静态页面面和代理服务器是同源的,然后代理服务器再向后端服务器发请求,服务器和服务器之间不存在同源限制。
说一说BFC
概念
BFC(Block Formatting Context)块级格式化上下文,是Web页面中一块独立的渲染区域,内部元素的渲染不会影响边界以外的元素。
触发BFC的条件
- float属性不为none
- position为absolute或fixed
- display为inline-block、table-cell、table-caption、flex、inline-flex
- overflow不为visible
BFC元素的特性
- 在BFC中,盒子中的元素从顶部开始垂直的一个接一个的排列
- 盒子垂直方向的距离由margin决定。同一个BFC的两个相邻盒子margin会重叠
- BFC中,margin-left会触碰到border-left(对于从左至右的方式,反之)
- BFC区域不会与浮动的盒子产生交集,而是紧贴边缘浮动
- 计算BFC高度时,自然会检测浮动的盒子高度
主要用途
- 清除内部浮动,父元素设置为BFC可以清除子元素的浮动(最常用overflow:hidden,IE6需加上*zoom:1):计算BFC高度时会检测浮动子盒子高度
- 解决外边距合并问题
- 右侧盒子自适应:BFC区域不会与浮动盒子产生交集,而是紧贴浮动边缘
说一说样式优先级的规则是什么?
CSS样式的优先级应该分成四大类
- 第一类
!important,无论引入方式是什么,选择器是什么,它的优先级都是最高的。 - 第二类引入方式,行内样式的优先级要高于嵌入和外链, 嵌入和外链如果使用的选择器相同就看他们在页面中插入的顺序,在后面插入的会覆盖前面的。
- 第三类选择器,选择器优先级:id选择器>(类选择器 | 伪类选择器 | 属性选择器 )> (后代选择器 | 伪元素选择器 )> (子选择器 | 相邻选择器) > 通配符选择器 。
- 第四类继承样式,是所有样式中优先级比较低的。
- 第五类浏览器默认样式优先级最低。
加分回答 使用!important要谨慎
- 一定要优先考虑使用样式规则的优先级来解决问题而不是
!important - 只有在需要覆盖全站或外部 CSS 的特定页面中使用
!important - 永远不要在你的插件中使用
!important - 永远不要在全站范围的 CSS 代码中使用
!important优先级的比较指的是相同的样式属性,不同样式属性优先级比较失效,比如:在设置max-width时注意,已经给元素的max-width设置了!important但是还不生效,很有可能就是被width覆盖了 举例:div最终的宽度还是200pxdiv { max-width: 400px !important; height: 200px;background-color: tomato; width: 200px; }
说一说Vuex是什么,每个属性是干嘛的,如何使用 ?
概念
Vuex是Vue中用来集中管理公共数据的工具。我们把这个工具称之为store,主要有state、mutation、action、module、getter属性组成。
store
store通过下面的方式创建
import { createStore } from 'vuex;
const options = {
state,
mutation,
getter,
action,
module
}
const store = createStore(...options)
state
Vuex store 实例的根 state 对象,用来存储公共管理的数据。
import { createStore } from 'vuex;
const store = createStore({
state: {
count: 1
}
})
mutation
mutation用来定义改变state中数据的方法,并且是唯一改变state中数据的途径。(注意:在mutation中不能存在异步任务,比如ajax请求,原因是mutation里的方法是纯函数, 如果在mutation中添加了异步任务,那样数据就不可跟踪了)。
import { createStore } from 'vuex;
const store = createStore({
state: {
count: 1
},
mutations: {
increment: (state) => {
state.count ++;
}
}
})
action
action属性类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。
import { createStore } from 'vuex';
const store = createStore({
state: {
count: 1,
},
mutations: {
increment: (state) => {
state.count ++;
}
},
actions: {
increment: (context) => {
context.commit('increment')
}
}
})
getter
getters 属性可以认为是定义 store 的计算属性。就像计算属性(computed)一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
import { createStore } from 'vuex';
const store = createStore({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getter: {
doneTodos: (state) => {
return state.todos.filter(todo => todo.done);
}
}
})
module
moudle属性是将store分割成模块。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块,从上至下进行同样方式的分割。
import { createStore } from 'vuex';
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态