四、JavaScript(ES6)
1、模块化
2、变量声明
- let const、var 使用场景对比
| 场景 | let | const | var |
|---|---|---|---|
| 定义层面 | 定义变量,只能在块作用域访问 不能跨函数 | 定义的是常量,使用的时候必须初始化 只能在块作用域访问 不能修改 | 定义的是变量,是全局的 没有块的概念,可以跨块访问 但是不能跨函数 |
3、数组扩展
1、扩展运算符...
- 扩展运算符是三个点(...)
- 就好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列
什么是rest参数(...rest)
rest参数(形式为"...变量名"),其中rest参数搭配的变量是一个数组,可以使用数组的一切操作。
console.log(...[1, 2, 3]);
// 1 2 3
console.log(1,...[2, 3, 4],5);
// 1 2 3 4 5
-
适用的一些场景
-
场景一、将一个数组添加到另一个数组尾部
// ES5
var arr1 =[1,2,3];
var arr2 =[4,5,6];
Array.prototype.push.apply(arr1, arr2);
console.log(arr1);
// [1,2,3,4,5,6]
// ES6
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);
console.log(arr1, "合并数组");
// [1, 2, 3, 4, 5, 6]
- 场景二、用于合并数组
[1,2].concat(more) // ES5
[1,2,...more] //ES6
- 场景三、可以将字符串转成数组
// ES5
const str="hello";
const a=str.split('');
console.log(b);
// ["h", "e", "l", "l", "o"]
// ES6
const str = "hello";
const b = [...str];
console.log(b);
// ["h", "e", "l", "l", "o"]
- 场景四、与解构赋值结合
// ES5
a = list[0],rest = list.slice(1)
// ES6
[a,...rest]=list
// 一些常见的例子
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest);// [2,3,4,5]
const [first,...rest]=[];
console.log(first); // undefined
console.log(rest);// []
const[first,...rest]=["foo"];
console.log(first); // "foo"
console.log(rest); // []
// !!!如果将扩展运算符用于数组赋值,只能放在参数的最后一位,不然回报错
// 例如
const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错
const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 报错
- 场景五、复制数组
//ES5
const a1=[1,2];
const a2=a1.concat();
a2[0]=2
[1,2]
//ES6
const a1=[1,2];
// 写法一
const a2=[...a1];
// 写法二
const[...a2]=a1;
4、函数扩展
4.1、箭头函数和不同函数的区别
4.2、解构函数
5、Promise
6、Class
五、浏览器基础
1、浏览器渲染原理
渲染一个html页面都做了哪些步骤
- 生成DOM树
- 生成CSS树
具体渲染过程 - 页面加载时,浏览器把获取到的HTML代码会解析成1个DOM树;
- DOM树里面会包含所有HTML标签、JS动态添加的元素等;
- 那么接着就是CSSOM树的构建,也就是CSS下载完后,对CSS进行解析成对象,组成CSSOM树;
- DOM树与CSSOM树结合,生成渲染树Render Tree;
回流和重绘
请描述下叫做回流?
回流是整个页面进行重排,也就是页面所有dom元素渲染。影响了这个页面的尺寸、布局、隐藏等。
请描述下是重绘?
当Render Tree树中某个DOM元素的属性需要更新,是影响外观且不影响布局的,则称之为重绘。
实际开发中如何避免回流的发生
- 尽可能在DOM末梢通过改变class来修改元素的style属性,尽可能减少受影响的dom元素;
- 避免内联样式,使用class的样式进行设置样式;
- 减少元素批量修改的次数
- 设置动画元素position属性为fixed或者absolute。由于当前元素从DOM流中独立出来,因此受影响的只有当前元素,元素repaint;
- CSS硬件加速 transform/opacity/filters...... 但如果用得不好就会引起内存过大,消耗性能等麻烦;
- 动画精度太强,会造成更多次的重绘/回流,必要的时候牺牲精度,满足性能损耗,获取性能和平滑度平衡;
- 避免使用tale进行布局,改用div;
2、浏览器缓的存储
说一下Cookie、sessionStorage、localstorage、indexDB区别
| 场景 | sessionStorage | localstorage | Cookie | indexDB |
|---|---|---|---|---|
| 数据生命周期 | 页面关闭就清理 | 除非人为清理, 否则都会一直存在 | 服务器生成,可以设置过期时间 在所有同源窗口 中都是共存的 | 除非人为清理, 否则都会一直存在 |
| 数据存储大小 | <=5M | <=5M | <4k | 无限 |
| 与服务端通信 | 不参与 | 不参与 | 会携带在header中,对于请求性能影响 | 不参与 |
| 使用场景 | 拥有独立特性的数据 | 持久保存的数据 | 保存回话信息 | - |
说说Cookie是一个什么东西?
是网站为了标识用户身份而储存在用户本地终端的数据,通过同源的http携带传递。记录浏览器和服务端间来回的传递。
Cookie也是会存在安全性的问题,说说怎么解决?
- 设置httpOnly,减少XSS攻击。
- 设置SameSite,可以让cookie不随跨站请求发出。
3、事件机制
3.1、事件触发三个阶段
3.2、注册事件
3.3、事件委托/事件代理
4、Event Loop
进程和线程
- 进程(资源分配的最小单位)
- 1、 应用程序的执行实例。
- 2、每一个进程都是由私有的虚拟地址空间、代码、数据、和和其他系统资源组成。
- 线程(程序分配的最小单位)
- 1、线程是进程内的一个独立执行单元,在不同的线程之间是可以共享进程资源的。
| 场景 | 进程 | 线程 |
|---|---|---|
| 拥有独立的堆栈空间和数据段 | 拥有独立的堆栈空间,但是共享数据段 | |
| 启动新的进程必须分配独立的空间 还要建立众多的数据表维护代码段、堆栈段、数据段 | 彼此之前使用相同的地址空间, 共享大部分数据, |
浏览器中的Event Loop
执行栈与事件队列
-
当js代码执行的时候会将不同的变量存于内存中的不同位置,堆(heap)/栈(stack)中加以区分,
-
堆存放一些对象,栈里存在一些基础的类变量、对象指针,
-
当所有的同步任务都在主线程上执行时,这些任务被排列在一个单独的地方,形成一个执行栈
-
js引擎在解析代码段时,会将同步任务顺序加入执行栈依次执行,
-
当遇到异步并行的时候,并不会一直等待,而是将异步任务挂起,--->先执行同步任务--->
宏任务(marco task)、微任务(micro task)
-
异步任务又分为宏任务和微任务,执行方式,microtask->marotask
-
宏任务、微任务分类
- microtask-> process.nextTick(node独有), Promises, Object.observe(废弃), MutationObserver
- marotask-> script(整体代码), setTimeout, setInterval, setImmediate(node独有), I/O, UI rendering
Node中的Event Loop
- timers,执行定时器队列中的回调,setTimeout()....
- i/o callbacks,这个阶段执行几乎所有的回调,但是不包括close事件,定时器和setImmediate()的回调
- idle,prepare,这个阶段仅在内部使用,
- poll,等待i/o事件,node在一些特殊情况下阻塞在这里
- cheack,setImmediate()的回调在这阶段执行
- close callbacks,
举个例子(2)
MicroTask队列与MacroTask队列
setTimeout(function () {
console.log(1);
});
console.log(2);
process.nextTick(() => {
console.log(3);
});
new Promise(function (resolve, rejected) {
console.log(4);
resolve()
}).then(res=>{
console.log(5);
})
setImmediate(function () {
console.log(6)
})
console.log('end');
2,4,end,3,5,1,6
参考文献
浏览器的event loop和node的event loop
5、浏览器跨域
同源策略
不同域的客户端脚本在无明确授权的情况下,不能读取对方资源。
产生跨域情况
同一协议、同一域名、同一端口,如果当其中一个不满足的时候,就会产生跨域问题。
解决跨域的方案
方案一、jsonp
-
实现原理
- 利用js标签(script,img, iframe)里面的跨域特性进行跨域数据访问。
- js标签里面存在的是一个跨域的url,实际执行的时候通过这个url获得一段字符串。
- 返回的字符串必须是一个合法的js调用,通过eval这个字符串来完成对获得数据的处理。
-
实现方式
//js原生实现jsonp
// A部分中的html代码
<script type="text/javascript">
function callback(data) {
alert(data.message);
}
//添加<script>标签的方法
function addScriptTag(src){
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function(){
addScriptTag("http://localhost:20002/test.js");
}
</script>
// B部分中获取数据部分js
//调用callback函数,并以json数据形式作为阐述传递,完成回调
callback({message:"success"});
- jq封装实现jsonp
$.ajax({
url: "http://localhost:9090/student",
type: "GET",
dataType: "jsonp", //指定服务器返回的数据类型
jsonp:"thefun" ,// 指定参数名称
jsonpCallback:"showData", // 指定回调函数名称
success: function (data) {
var result = JSON.stringify(data); //json对象转成字符串
$("#text").val(result);
}
});
- jsonp,只能使用get方式发起跨域请求,跨域请求需要服务端配合,要设置callback,才能完成跨域请求。
方案二、cors
- 分类
- 1.1、简单请求
- 请求方法,主要是head,get,post,三种之一的。
- HTTP的头信息不超出这几种字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
- 1.2、非简单请求
- 预检请求,是对服务器有特殊要求的请求,比如put\delete,或者就是Content-Type:application/json;
- 浏览器先询问服务器,当网页所在的域名是否在服务器的许可名单,以及可以使用那些HTTP动词和信息字段,得到肯定答复,浏览器才会发出正式XMLHttpRequest请求。
- 预请求基础设置,一般使用方法,options。主要是在origin\access-Control-Request-Method\access-Control-Request-Headers
-
前端配置
- 为了要携带cookie, withCredentials 为true
-
后端配置
-
origin // 跨域请求来自哪里
-
access-control-allow-methods // 请求可能用到的什么方法 get put post
-
access-Control-Request-Headers // 请求时可能用到的头部
-
access-Control-Allow-Origin // * 表示任何请求都可访问
-
access-Control-Allow-Credentials // 是否允许发送cookie、 true
-
access-control-allow-headers // 表示跨域支持的请求头有哪些
-
方案三、nginx
通过反向代理,使得请求地址和服务器同域
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
...
location /api {
proxy_pass https://b.test.com; # 设置代理服务器的协议和地址
proxy_cookie_domain b.test.com a.test.com; # 修改cookie,针对request和response互相写入cookie
}
跨域方案对比
cors、nginx做对比
| 场景 | cors | nginx |
|---|---|---|
| 前端 | withCredentials 为true | 无 |
| 后端 | setHeader:ACA-Origin、ACA-Method、 ACA-Credentials等 | 无 |
| 服务器 | 无 | Nginx配置 |
| 移值性 | 高、无需额外配置 | 低,每套环境可能均不相同 |
| 安全性 | 来源可控,直接追溯 | 通过X-Forward-For追溯多级来源 ( X-Forwarded-For:client, proxy1, proxy2) |
| 扩展性 | 黑白名单控制 | 更新配置,跨域模型会产生变化 |