跨域怎么做的
- JSONP
所谓的JSONP就是前端通过script标签不受通源策略的影响的特性,向API接口拼接一个回调发送给服务端,然后服务端拿到这个回调,执行拼将需要的数据放进回调的参数里,最后服务端将该函数返回给浏览器,所以这种跨域方式需要前端和后端配合 - CORS
所谓的CORS就是服务端通过设置响应头实现(比如Access-Control-Allow-Origin:也就是设置给那个请求域响应数据;Access-Control-Allow-Methods:给请求域指定能够使用哪些方法来请求该服务端),所以这种方法,服务端可单方面完成 - Node接口代理
还是回到同源策略,同源策略它只是浏览器的一个策略而已,它是限制不到后端的,也就是前端-后端会被同源策略限制,但是后端-后端则不会被限制,所以可以通过Node接口代理,先访问已设置Cors的后端1,再让后端1去访问后端2获取数据到后端1,后端1再把数据传到前端
scss用过哪些功能
- 使用变量: SCSS中的变量以$开头
$border-color:#aaa; //声明变量
.container {
$border-width:1px;
border:$border-width solid $border-color; //使用变量
}
- 混合器(函数)
2.1 声明一个函数
使用@mixin指令声明一个函数,看一下自己的css文件,有重复的代码片段都可以考虑使用混合器将他们提取出来复用。
@mixin border-radius{
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
color:red;
}
混合器作用域内的属性都是return的值,除此之外,还可以为函数传参数。
@mixin get-border-radius($border-radius,$color){
-moz-border-radius: $border-radius;
-webkit-border-radius: $border-radius;
border-radius: $border-radius;
color:$color;
}
// 也可以设置混合器的默认值。
@mixin get-border-radius($border-radius:5px,$color:red){
-moz-border-radius: $border-radius;
-webkit-border-radius: $border-radius;
border-radius: $border-radius;
color:$color;
}
2.2 使用函数: 使用函数的关键字为@include
.container {
border:1px solid #aaa;
@include get-border-radius; //不传参则为默认值5px
@include get-border-radius(10px,blue); //传参
}
/*多个参数时,传参指定参数的名字,可以不用考虑传入的顺序*/
.container {
border:1px solid #aaa;
@include get-border-radius; //不传参则为默认值5px
@include get-border-radius($color:blue,$border-radius:10px); //传参
}
tips:混合器中可以写一切scss代码。
- 继承
继承是面向对象语言的一大特点,可以大大降低代码量。
3.1 定义被继承的样式
%border-style {
border:1px solid #aaa;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}
使用%定义一个被继承的样式,类似静态语言中的抽象类,他本身不起作用,只用于被其他人继承。
3.2 继承样式
通过关键字@extend即可完成继承。
.container {
@extend %border-style;
}
上述例子中看不出混合器与继承之间的区别,那么下一个例子可以看出继承与混合器之间的区别。
.container {
@extend %border-style;
color:red;
}
.container1 { //继承另一个选择器
@extend .container;
}
垃圾回收机制
根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同分代采用不同的回收算法
- 新生代采用空间换时间的 scavenge 算法:整个空间分为两块,变量仅存在其中一块,回收的时候将存活变量复制到另一块空间,不存活的回收掉,周而复始轮流操作
- 老生代使用标记清除和标记整理,标记清除:遍历所有对象标记标记可以访问到的对象(活着的),然后将不活的当做垃圾进行回收。回收完后避免内存的断层不连续,需要通过标记整理将活着的对象往内存一端进行移动,移动完成后再清理边界内存
深拷贝
//方法一
function deepCopy(oldObj) {
let newObj = {};
for(let key in oldObj) {
if(typeof oldObj[key] === 'object') {
newObj[key] = deepCopy(oldObj[key]);
}else{
newObj[key] = oldObj[key];
}
}
return newObj;
}
//方法二
//优点:简单方便,大多数时候可以满足需求
//缺点:1.无法复制函数 2.原型链没了,对象就是object,所属的类没了。
function deepCopy2(oldObj) {
let newObj = {}
newObj = JSON.parse( JSON.stringify(oldObj) )
return newObj
}
用Promise实现图片的异步加载
let imageAsync=(url)=>{
return new Promise((resolve,reject)=>{
let img = new Image();
img.src = url;
img.οnlοad=()=>{
console.log(`图片请求成功,此处进行通用操作`);
resolve(image);
}
img.οnerrοr=(err)=>{
console.log(`失败,此处进行失败的通用操作`);
reject(err);
}
})
}
imageAsync("url").then(()=>{
console.log("加载成功");
}).catch((error)=>{
console.log("加载失败");
})
简述事件循环原理
事件循环的原因: js的本职工作是为了处理dom元素,因此不能是一个多线程的语言,例如这个操作删除了某个dom元素,而其他操作正在移动这个元素,会造成一些不可预见的问题,因此js从开始就是一个单线程语言。
JavaScript 只有一个主线程和一个执行栈。
当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
虚拟dom是什么? 原理? 优缺点?
Virtual DOM是对DOM的抽象,本质上是JavaScript对象,这个对象就是更加轻量级的对DOM的描述。
-
真实DOM的优势:易用
-
真实DOM的缺点:效率低,解析速度慢,内存占用量过高;性能差,频繁操作真实DOM,易于导致重绘与回流。
-
虚拟DOM的优势:
简单方便:如果使用手动操作真实DOM来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
性能方面:使用Virtual DOM,能够有效避免真实DOM数频繁更新,减少多次引起重绘与回流,提高性能
跨平台:React借助虚拟DOM, 带来了跨平台的能力,一套代码多端运行
- 虚拟DOM的缺点:
在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
首次渲染大量DOM时,由于多了一层虚拟DOM的计算,速度比正常稍慢
async await对比promise的优缺点
async/await优点:
- 它做到了真正的串行的同步写法,代码阅读相对容易
- 对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面
function a() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(222)
}, 2222)
})
};
async function f() {
try {
if ( await a() === 222) {
console.log('yes, it is!') // 会打印
}
} catch (err) {
// ...
}
}
- 处理复杂流程时,在代码清晰度方面有优势
async/await缺点:
-
无法处理promise返回的reject对象,要借助
try...catch...
-
用 await 可能会导致性能问题,因为 await 会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性。promise 可以通过all解决并发问题(
Promise.all([ajax1(), ajax2()])
)。 -
try...catch...
内部的变量无法传递给下一个try...catch...
,Promise和then/catch
内部定义的变量,能通过then链条的参数传递到下一个then/catch
,但是async/await的try内部的变量,如果用let和const定义则无法传递到下一个try...catch...
,只能在外层作用域先定义好。
但async/await确确实实是解决了promise一些问题的。更加灵活的处理异步
promise的一些问题:
- 一旦执行,无法中途取消,链式调用多个then中间不能随便跳出来
- 错误无法在外部被捕捉到,只能在内部进行预判处理,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
- Promise内部如何执行,监测起来很难,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
防抖和节流
- 函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
- 函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
// 防抖
function debounce(fn, date) {
let timer //声明接收定时器的变量
return function (...arg) { // 获取参数
timer && clearTimeout(timer) // 清空定时器
timer = setTimeout(() => { // 生成新的定时器
//因为箭头函数里的this指向上层作用域的this,所以这里可以直接用this,不需要声明其他的变量来接收
fn.apply(this, arg) // fn()
}, date)
}
}
//--------------------------------
// 节流
function debounce(fn, data) {
let timer = +new Date() // 声明初始时间
return function (...arg) { // 获取参数
let newTimer = +new Date() // 获取触发事件的时间
if (newTimer - timer >= data) { // 时间判断,是否满足条件
fn.apply(this, arg) // 调用需要执行的函数,修改this值,并且传入参数
timer = +new Date() // 重置初始时间
}
}
}