回调地狱及解决办法
回调函数中嵌套回调函数这种情况就称为回调地狱,就是为了让回调函数满足异步的情况(比如setTimeout里面的先执行再执行后面的函数,一般来说就要嵌套setTimeout也就是回调地狱), (回调函数就是当一个函数作为参数传入另一个参数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行。比如setTimeout(function(){xxx},3000)表示三秒后执行function,这就是一种回调函数)
回调地狱的解决办法:
法一:Promise
Promise有两个参数:resolve和reject,异步执行成功调用resolve,失败调用reject; Promise对象的then方法处理成功响应,catch处理失败响应; 注意每次then之后都要return一个Promise对象,方便下一次then接受数据
function fn(str){
var p=new Promise(function(resolve,reject){
//处理异步任务
var flag=true;
setTimeout(function(){
if(flag){
resolve(str)
}
else{
reject('error')
}
})
})
return p;
}
fn('111')
.then((data)=>{
console.log(data);
return fn('222');
})
.then((data)=>{
console.log(data);
return fn('333')
})
.then((data)=>{
console.log(data);
})
.catch((data)=>{
console.log(data);
})
//打印结果就是111 222 333依次打印
Promise的三个缺点 1)无法取消Promise,一旦新建它就会立即执行,无法中途取消 2)如果不设置回调函数,Promise内部抛出的错误,不会反映到外部 3)当处于pending状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成
法二:async/await
async表示异步任务,即不会阻塞后面函数的执行 比如:
async function fn() {
var flag = true;
if (flag) {
return '222';
}
else{
throw 'error'
}
}
fn()
.then(data=>{
console.log(data);
})
.catch(data=>{
console.log(data);
})
console.log('111');
而await只能在async定义的函数中使用,不能单独使用; await后面可以直接跟一个 Promise实例对象(可以跟任何表达式,更多的是跟一个返回Promise对象的表达式); await可以直接拿到Promise中resolve中的数据。
//封装一个返回promise的异步任务
function fn(str) {
var p = new Promise(function (resolve, reject) {
var flag = true;
setTimeout(function () {
if (flag) {
resolve(str)
} else {
reject('error')
}
})
})
return p;
}
//封装一个执行上述异步任务的async函数
async function test(){
var res1=await fn('111');
//await直接拿到fn()返回的promise的数据,并且赋值给res
var res2=await fn('222');
var res3=await fn('333');
console.log(res1,res2,res3);
}
//执行函数
test();
详细见原文: 一文告诉你什么是回调地狱,如何解决回调地狱
箭头函数
1、箭头函数没有this,由作用域链来确定this的值 2、箭头函数没有自己的arguments对象,但是可以访问外围函数的arguments对象 3、不能通过new关键字调用,同样也没有new.target值和原型 实例: 4、如果形参只有一个,则小括号可以省略; 5、函数体如果只有一条语句,则花括号可以省略,并省略return,函数的返回值为该条语句的执行结果;
const multiply = num => num * num;
等价于
const multiply = function (num) {
return num * num;
};
多参数的情况
// 多参数
const multiply = (num1, num2) => num1 * num2;
// 可变参数
const sum = (num1, num2, ...rest) => {
let result = num1 + num2;
for (let i = 0; i < rest.length; i++) {
result += rest[i];
}
return result;
};
需要...表示不确定参数的个数,而且还需要return 可以看出箭头函数代码很简洁,那么有没有不能使用箭头函数的地方呢
不适用的情况如下
1.对象方法
const obj = {
x: 1,
print: () => {
console.log(this === window); // => true
console.log(this.x); // undefined
}
};
obj.print();
this指向的是它的上下文,也就是window,所以无法找到obj里面的x,只能用普通函数的方法:
print () {
console.log(this === test); // => true
console.log(this.x); // 1
}
2.原型方法也会出现同样的问题:
关于原型链的考点:原型链最终指向NULL 原型的介绍和用法: 函数原型
function Cat (name) {
= name;
}
Cat.prototype.sayCatName = () => {
console.log(this === window); // => true
return ;
};
const cat = new Cat('Miao');
cat.sayCatName(); // => undefined
解决办法:
Cat.prototype.sayCatName = function () {
console.log(this === cat); // => true
return ;
};
3.事件的回调
如:
const btn = document.getElementById('myButton');
btn.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
当为一个 DOM 事件绑定回调函数后,触发回调函数时的 this,需要指向当前发生事件的 DOM 节点,也就是这里的 btn。当回调发生时,浏览器会用 btn 的上下文去调用处理函数。所以最后的 this.innerHTML 等价于 window.innerHTML,问题就在这里。
btn.addEventListener('click', function() {
console.log(this === btn); // => true
this.innerHTML = 'Clicked button';
});
4.构造函数
const Message = (text) => {
this.text = text;
};
var helloMessage = new Message('Hello World!');
// Uncaught TypeError: Message is not a constructor
会报错,反正构造函数不能用箭头函数就完事了 原文出处: 10分钟理解ES6箭头函数
link和import的区别
区别1:link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。
区别2:link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
区别3:link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
区别4:link支持使用Javascript控制DOM去改变样式;而@import不支持。
区别5:link引入的样式权重大于@import引入的样式。
常见的浏览器内核
使用Trident内核的浏览器:IE、Maxthon、TT、The World等;
使用Gecko内核的浏览器:Netcape6及以上版本、FireFox、MozillaSuite/SeaMonkey;
使用Presto内核的浏览器:Opera7及以上版本;
使用Webkit内核的浏览器:Safari、Chrome。
css的重绘与回流
重绘:当节点需要更改外观而不会影响布局,就是对某一元素进行单独渲染 eg. 改变元素颜色
回流:DOM结构的修改引发DOM几何尺寸变化的时候,发生回流。 常见的几何属性有width、height、padding、margin、left、top、border 或者是DOM节点发生增减移动,即要重新渲染整个DOM树。 eg. (1)添加或者删除可见的 DOM 元素(不可见元素不会触发回流); (2)元素尺寸或位置发生改变 (3)元素内容变化,比如文字数量或图片大小 (4)浏览器窗口大小发生改变 (5)CSS伪类的激活(例如::hover,从而改变了元素的布局的) 回流一定重绘,重绘不一定回流。
减少重绘和回流的办法。 使用css3新增属性:translate替代top等方向值。 避免频繁使用style,而是采用class。
let、var、const的区别
var 没有块级作用域,支持变量提升。
let 有块级作用域,不支持变量提升。不允许重复声明,暂存性死区。不能通过window.变量名进行访问.
const 有块级作用域,不支持变量提升,不允许重复声明,暂存性死区。声明一个变量一旦声明就不能改变,改变报错。
简言之: 只有var可以预解析提前调用,就是结果是undefined,另外两个都会报错;
只有var可以重复定义,相当于重复赋值,另两个报错;
const是常量,不可更改;
let和const定义在函数中(局部作用域)就不能在外面使用(全局作用域);
循环中var是不断覆盖上一次的值,所以最后var保存的变量是最后一个值,而let每次相当于独立的变量(相当于每个循环都是一个作用域,所以let声明的变量在每一次循环中是不同的变量)
let和const存在暂存死区,var没有
instanceof和typeof
instanceof返回的是bool,表示这个数据是否是对象实例; typeof返回的是数据类型 (笔试完才看见这个555,笔试的时候写的instanceof返回是什么对象的实例了)
Ajax、Axios、Fetch有啥区别?
Ajax:是对XMLHttpRequest对象(XHR)的封装 Axios:是基于Promise对XHR对象的封装 Fetch:是window的一个方法,也是基于Promise,但是与XHR无关,不支持IE 参考: Ajax、Axios、Fetch
0.1 + 0.2 === 0.3是错误的
JavaScript的计算存在精度丢失问题
原因:JavaScript中小数是浮点数,需转二进制进行运算,有些小数无法用二进制表示,所以只能取近似值,所以造成误差 解决方法: 先变成整数运算,然后再变回小数 toFixed() 性能不好,不推荐
冒泡
这里的冒泡可不是冒泡排序的冒泡,js中冒泡是指事件会向上传递,比如,我点击了div,触发了div的click事件,同时也会触发body的click事件 xxx.addEventListence('click', function(){}, false) 这里的false就是指不阻止冒泡,默认就是false
MVC模型
Model View Controller 模型、视图和控制器 servlet属于控制器Controller jsp属于视图View JavaBean属于模型Model
怪异盒模型(IE盒)
box-sizing:border-box; 其中box的width=border+padding + content 默认模式是 box-sizing:content-box 其中box的width=content
js中原始数据类型
Undefined,Null,Boolean,Number、String。 引用数据类型: 对象、数组、函数
slice和splice区别:
slice不会更改原数组,splice会 slice(start,end); splice(start,deleteCount,item1,item2…..);
css选择器优先级
!important > 行内样式 > ID选择器 > 类选择器 > 元素 > 通配符* > 继承 > 浏览器默认属性
visibility:hidden 和 display:none区别
1.display:none是彻底消失,不在文档流中占位,浏览器也不会解析该元素;
2.visibility:hidden是视觉上消失了,可以理解为透明度为0的效果,在文档流中占位,浏览器会解析该元素;
清除浮动的几种方式
1.额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐)
2.父级添加overflow属性(父元素添加overflow:hidden)(不推荐)
3.clearfix:after
clearfix{
*zoom:1;
}
.clearfix:after{
content:'clear';
display:block;
height:0;
clear:both;
overflow:hidden;
visibility:hidden;
}
HTTP1.0和HTTP1.1的区别
1.缓存处理:1.1提供了更多可供选择的缓存头来控制缓存策略 2.长连接:1.1支持长连接和请求的流水线处理 3.请求方法:1.1新增了PUT和DELETE两种请求方法 4.错误通知的管理:1.1中新增了24个错误状态响应码 5.Host头处理:1.1的请求消息和响应消息都支持host域,且请求消息中如果没有host域会报告一个错误(400 Bad Request) 6.带宽:1.1更节约带宽
HTTP1.1和HTTP2.0的区别
1.多路复用:2.0采用多路复用技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。 2.头部数据压缩:2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。 3.服务器推送:2.0引入了server push,它允许服务端推送资源给浏览器,在浏览器明确地请求之前,免得客户端再次创建连接发送请求到服务器端获取。这样客户端可以直接从本地加载这些资源,不用再通过网络。
HTTPS与HTTP的一些区别
1.HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。
2.HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
3.HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4.HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。
七层网络模型
物理层->数据链路层->网络层->传输层->会话层->表示层->应用层
会话层: 会话层建立、管理和终止表示层与实体之间的通信会话; 建立一个连接(自动的手机信息、自动的网络寻址); 表示层: 表示层,供多种功能用于应用层数据编码和转化,以确保以一个系统应用层发送的信息 可以被另一个系统应用层识别; 可以理解为:解决不同系统之间的通信,eg:Linux下的QQ和Windows下的QQ可以通信;