什么是闭包:函数使用了不属于自己的局部变量(函数套函数,里面函数使用了外面函数定义的变量)
闭包的作用:避免全局污染
闭包的缺点:使用过多会造成内存泄漏(占用的内存释放不掉)
仅在当前会话下生效,当你关闭页面或浏览器后你存储的sessionStorage数据会被清除。
可存储的数据大小一般在5mb。
不参与和服务器的通信
永久有效,关闭浏览器也不会消失的,除非自己主动清除localStorage信息。
可存储的数据大小一般在5mb。
不参与和服务器的通信
cookie是在设置的过期时间之前一直有效,哪怕关闭浏览器。
有个数限制(各浏览器不同),一般不能超过20个。
与服务器端通信:每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题
只有函数才有原型,原型是用来存放共有属性的,原型是一个属性,它的值是个对象
每一个对象都有一个__proto__(隐式原型)的属性,这个属性指向创建该对象的构造函数的prototype(如果不能确定他是谁的实例,都是Object的实例)
原型链:对象实例的隐士原型指向创建改对象的构造函数的原型对象,这样一层一层的指向关系形成的链叫原型链。如下。
es5this指向函数运行时所在的对象(谁调用我我指谁,会随着调用者不同而改变)
es6箭头函数中this指向函数定义时所在的对象(我在哪出生我指谁,永远不会改变)
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。存在跨域的情况:
网络协议不同,如http协议访问https协议。
端口不同,如80端口访问8080端口。
域名不同,如qianduanblog.com访问baidu.com。
子域名不同,如abc.qianduanblog.com访问def.qianduanblog.com。
域名和域名对应ip,如www.a.com访问20.205.28.90.
解决方案
1.proxy代理(vue中常用)
2.jsonp
3.生产环境上用nginx反向代理
call和appley的作用都是改变this指向,让函数中的this可以自定义为我们传入的对象。
call和apply作用是一样的,只不过call接受的是参数序列当参数,apply接受的是数组当参数
其实call和apply的意思就是让一个对象去调用别人的方法,之前我们大多接触的是对象调用自己的方法。
就比如你朋友买了台车,正常我们接触的都是自己开自己的车,但是现在你想开你朋友的车,就是你这个对象去调用别人的方法
fn.call(obj,arg1,arg2,arg3)
fn.apply(obj,[arg1,arg2,arg3])
开车这个函数.call(开车的这个对象,参数1,参数2) **
开车这个函数.apply(开车的这个对象,[参数1,参数2]) **
call前面是你要调用的方法,括号里是要调用这个方法的对象和参与运算的参数
例如:
function fn(){
console.log(this.name+"在开车")
}
var obj={
name:"小明"}
fn.call(obj)
运行结果:
上面的例子我们是用小明对象调用了window对象中定义的方法。下面换个对象是一样的道理,然后我们把参数传进去
var xh={
name:"小红",
fn:function(car){
console.log(this.name+"在开"+car+"车")
}
}
var obj={
name:"小明"}
xh.fn.call(obj,"宝马")
结果如下
换成apply,用法一样,只是传参换成了数组,注意call方法也支持多个参数,只不过不能写成数组,要写成以逗号分隔
var xh={
name:"小红",
fn:function(car1,car2){
console.log(this.name+"在开"+car1+car2+"车")
}
}
var obj={
name:"小明"}
xh.fn.apply(obj,["宝马","奔驰"])
总结:面试一般问作用和区别,作用是都是用来改变this指向的,区别是call接受参数序列,apply接受的是数组
for-in是ES5标准,遍历的是key(可遍历对象、数组或字符串的key);
for-of是ES6标准,遍历的是value(可遍历对象、数组或字符串的value)。
深拷贝递归地复制新对象中的所有值或属性,而浅拷贝只复制引用关系。
在深拷贝中,新对象中的更改不会影响原始对象,而在浅拷贝中,新对象中的更改,原始对象中也会跟着改。
在深拷贝中,原始对象不与新对象共享相同的属性,而在浅拷贝中,它们具有相同的属性。
slice方法和concat方法可以实现普通数组的深赋值,但是如果是二维数组就无法深复制
下面这个例子证明slice无法真正实现深复制
var arr1=[1,2,3,['1','2','3']];
var arr2=arr1.slice(0);
arr1[3][0]=0;
console.log(arr1);//[1,2,3,['0','2','3']]
console.log(arr2);//[1,2,3,['0','2','3']]
如何实现深拷贝:
1.JSON.stringfy JSON.parse
var arr1 = ['red','green'];
var arr2 = JSON.parse(JSON.stringify(arr1));//复制
console.log(arr2)//['red','green'];
arr1.push('black') ;//改变color1的值
console.log(arr2)//["red", "green"]
console.log(arr1)//["red", "green", "black"]
2.递归
function deepClone(obj){
//判断参数是不是一个对象
let objClone = obj instanceof Object?[]:{
};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
var a ={
x:1,
y:2
};
b=deepClone(a);
a.x=3
console.log(a);
console.log(b);
1- 输入网址:那肯定是输入你要访问的网站网址了,俗称url;
2- 缓存解析:它先去缓存当中看看有没有,从 浏览器缓存-系统缓存-路由器缓存 当中查看,如果有从 缓存当中显示页面,然后没有那就进行步骤三;
缓存就是把你之前访问的web资源,比如一些js,css,图片什么的保存在你本机的内存或者磁盘当中
3- 域名解析:域名到IP地址的转换过程。域名的解析工作由DNS服务器完成。解析后可以获取域名相应的IP地址
4- tcp连接:在域名解析之后,浏览器向服务器发起了http请求,tcp连接,三次握手建立tcp连接。TCP协议是面向连接的,所以在传输数据前必须建立连接
5- 服务器收到请求:服务器收到浏览器发送的请求信息,返回一个响应头和一个响应体。
6-页面渲染:浏览器收到服务器发送的响应头和响应体,进行客户端渲染,生成Dom树、解析css样式、js交互。
利用事件的冒泡传播机制(触发当前元素的某一个行为,它父级所有元素的相关行为都会被触发),如果一个容器中有很多元素都要绑定点击事件,我们没有必要一个个的绑定了,只需要给最外层容器绑定一个点击事件即可
什么是数组扁平化?
[‘a’,‘b’,‘c’] //这是一个拥有3个元素的数组,是一个一维数组(不存在数组嵌套)。[[‘a’,‘b’],[‘c’,‘d’],[‘e’,‘f’]] 从整体上看是一个数组,但是其中的元素又是数组,即数组中嵌套数组,这就是二维数组
以此类推·····
[‘a’,[‘b’,[‘c’]]]//3维数组 [‘a’,[‘b’,[‘c’,[…]]]]//n维数组 数组扁平化就是把多维数组转化成一维数组。
1.通过es5递归实现
let result = [];
function fn(ary) {
for(let i = 0; i < ary.length; i++) }{
let item = ary[i];
if (Array.isArray(ary[i])){
fn(item);
} else {
result.push(item);
}
}
}
2.es6 +reduce+递归实现
let arr=[[1,2,3],4,5,[6,7,[8,9]]];
function bianping(arr){
return arr.reduce((res,item) =>{
return res.concat(Array.isArray(item)?bianping(item):item)
},[])
}
console.log(bianping(arr));
见我的这篇博客
防抖和节流
js作为单线程语言。在执行过程中,会产生执行环境。这些执行环境中的代码被顺序的加入到执行栈中,如果遇到异步代码,会被挂起并加入到任务队列当中,等到主线程任务执行完毕,event loop就会从任务队列取出需要执行的代码放入到执行栈中执行。
异步任务分类为宏任务(macro-task)和微任务(micro-task)。
宏任务:整体的Script setTimeout setInterval
微任务:Promise process.nextTick
例子
// 这是一个同步任务
console.log('1') --------> 直接被执行
目前打印结果为:1
// 这是一个宏任务
setTimeout(function () {
--------> 整体的setTimeout被放进宏任务列表
console.log('2') 目前宏任务列表记为【s2】
});
new Promise(function (resolve) {
// 这里是同步任务
console.log('3'); --------> 直接被执行
resolve(); 目前打印结果为:1、3
// then是一个微任务
}).then(function () {
--------> 整体的then[包含里面的setTimeout]被放进微任务列表
console.log('4') 目前微任务列表记为【t45】
setTimeout(function () {
console.log('5')
});
});
有微则微,无微则宏
如果微任务列表里面有任务 会执行完毕后在执行宏任务。
浏览器瞅了一眼微任务列表 发现里面有微任务 就开始全部执行
then(function () {
console.log('4') --------> 直接被执行
目前打印结果为:1、3、4
setTimeout(function () {
--------> 被放进宏任务列表了
console.log('5') 目前宏任务列表记为【s2、s5】
});
});
浏览器发现微任务执行完毕了
开始执行宏任务列表
setTimeout(function () {
console.log('2') --------> 直接被执行
目前打印结果为:1、3、4、2
});
setTimeout(function () {
console.log('5') --------> 直接被执行
目前打印顺序为: 1、3、4、2、5、5
});
最终结果为: 1、3、4、2、5