JS面试题
# js常见面试题
# 1.1 延迟加载JS有哪些方式?
延迟加载:async、defer
例如:<script defer type="text/javascript" src='script.js'></script>
defer : 等html全部解析完成,才会执行js代码,顺次执行js脚本。
async : async是和html解析同步的(一起的),不是顺次执行js脚本(谁先加载完谁先执行)。
1
2
3
4
5
2
3
4
5
# 1.2 JS数据类型有哪些?
基本类型:string、number、boolean、undefined、null、symbol、bigint
引用类型:object
NaN是一个数值类型,但是不是一个具体的数字。
1
2
3
4
2
3
4
# 1.3 JS数据类型考题
# 考题一:
console.log( true + 1 ); //2
console.log( 'name'+true ); //nametrue
console.log( undefined + 1 ); //NaN
console.log( typeof undefined ); //undefined
1
2
3
4
2
3
4
# 考题二:
console.log( typeof(NaN) ); //number
console.log( typeof(null) ); //object
1
2
2
# 1.4 null和undefined的区别
1. 作者在设计js的都是先设计的null(为什么设计了null:最初设计js的时候借鉴了java的语言)
2. null会被隐式转换成0,很不容易发现错误。
3. 先有null后有undefined,出来undefined是为了填补之前的坑。
具体区别:JavaScript的最初版本是这样区分的:null是一个表示"无"的对象(空对象指针),转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。
1
2
3
4
5
2
3
4
5
# 1.5 ==和===有什么不同?
== : 比较的是值
string == number || boolean || number ....都会隐式转换
通过valueOf转换(valueOf() 方法通常由 JavaScript 在后台自动调用,并不显式地出现在代码中。)
=== : 除了比较值,还比较类型
1
2
3
4
5
6
2
3
4
5
6
# 1.6 JS微任务和宏任务
1. js是单线程的语言。
2. js代码执行流程:同步执行完==》事件循环
同步的任务都执行完了,才会执行事件循环的内容
进入事件循环:请求、定时器、事件....
3. 事件循环中包含:【微任务、宏任务】
微任务:promise.then
宏任务:setTimeout..
要执行宏任务的前提是清空了所有的微任务
流程:同步==》事件循环【微任务和宏任务】==》微任务==》宏任务=》微任务...
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 1.7 JS作用域考题
1. 除了函数外,js是没有块级作用域。
2. 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3. 注意声明变量是用var还是没有写(window.)
4. 注意:js有变量提升的机制【变量悬挂声明】
5. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
1
2
3
4
5
6
2
3
4
5
6
面试的时候怎么看:
1. 本层作用域有没有此变量【注意变量提升】
2. 注意:js除了函数外没有块级作用域
3. 普通声明函数是不看写函数的时候顺序
1
2
3
2
3
# 考题一:
function c(){
var b = 1;
function a(){
console.log( b );
var b = 2;
console.log( b );
}
a();
console.log( b );
}
c();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 考题二:
var name = 'a';
(function(){
if( typeof name == 'undefined' ){
var name = 'b';
console.log('111'+name);
}else{
console.log('222'+name);
}
})()
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 考题三:
function fun( a ){
var a = 10;
function a(){}
console.log( a );
}
fun( 100 );
1
2
3
4
5
6
2
3
4
5
6
# 1.8 JS对象考题
JS对象注意点:
1. 对象是通过new操作符构建出来的,所以对象之间不想等(除了引用外);
2. 对象注意:引用类型(共同一个地址);
3. 对象的key都是字符串类型;
4. 对象如何找属性|方法;
查找规则:先在对象本身找 ===> 构造函数中找 ===> 对象原型中找 ===> 构造函数原型中找 ===> 对象上一层原型查找
1
2
3
4
5
2
3
4
5
# 考题一:
[1,2,3] === [1,2,3] //false
1
# 考题二:
var obj1 = {
a:'hellow'
}
var obj2 = obj1;
obj2.a = 'world';
console.log(obj1); //{a:world}
(function(){
console.log(a); //undefined
var a = 1;
})();
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 考题三:
var a = {}
var b = {
key:'a'
}
var c = {
key:'c'
}
a[b] = '123';
a[c] = '456';
console.log( a[b] ); // 456
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 1.9 JS作用域+this指向+原型的考题
# 考题一:
function Foo(){
getName = function(){console.log(1)} //注意是全局的window.
return this;
}
Foo.getName = function(){console.log(2)}
Foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){
console.log(5)
}
Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo().getName();//3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 考题二:
var o = {
a:10,
b:{
a:2,
fn:function(){
console.log( this.a ); // 2
console.log( this ); //代表b对象
}
}
}
o.b.fn();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 考题三:
window.name = 'ByteDance';
function A(){
this.name = 123;
}
A.prototype.getA = function(){
console.log( this );
return this.name + 1;
}
let a = new A();
let funcA = a.getA;
funcA(); //this代表window
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 考题四:
var length = 10;
function fn(){
return this.length + 1;
}
var obj = {
length:5,
test1:function(){
return fn();
}
}
obj.test2 = fn;
console.log( obj.test1() ); //1
console.log( fn()===obj.test2() ); //false
console.log( obj.test1() == obj.test2() ); //false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.10 JS判断变量是不是数组,你能写出哪些方法?
# 方式一:isArray
var arr = [1,2,3];
console.log( Array.isArray( arr ) );
1
2
2
# 方式二:instanceof 【可写,可不写】
var arr = [1,2,3];
console.log( arr instanceof Array );
1
2
2
# 方式三:原型prototype
var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 );
1
2
2
# 方式四:isPrototypeOf()
var arr = [1,2,3];
console.log( Array.prototype.isPrototypeOf(arr) )
1
2
2
# 方式五:constructor
var arr = [1,2,3];
console.log( arr.constructor.toString().indexOf('Array') > -1 )
1
2
2
# 1.11 slice是干嘛的、splice是否会改变原数组
1. slice是来截取的
参数可以写slice(3)、slice(1,3)、slice(-3)
返回的是一个新的数组
2. splice 功能有:插入、删除、替换
返回:删除的元素
该方法会改变原数组
1
2
3
4
5
6
2
3
4
5
6
# 1.12 S数组去重
# 方式一:new set
var arr1 = [1,2,3,2,4,1];
function unique(arr){
return [...new Set(arr)]
}
console.log( unique(arr1) );
1
2
3
4
5
2
3
4
5
# 方式二:indexOf
var arr2 = [1,2,3,2,4,1];
function unique( arr ){
var brr = [];
for( var i=0;i<arr.length;i++){
if( brr.indexOf(arr[i]) == -1 ){
brr.push( arr[i] );
}
}
return brr;
}
console.log( unique(arr2) );
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 方式三:sort
var arr3 = [1,2,3,2,4,1];
function unique( arr ){
arr = arr.sort();
var brr = [];
for(var i=0;i<arr.length;i++){
if( arr[i] !== arr[i-1]){
brr.push( arr[i] );
}
}
return brr;
}
console.log( unique(arr3) );
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 1.13 找出多维数组最大值
function fnArr(arr){
var newArr = [];
arr.forEach((item,index)=>{
newArr.push( Math.max(...item) )
})
return newArr;
}
console.log(fnArr([
[4,5,1,3],
[13,27,18,26],
[32,35,37,39],
[1000,1001,857,1]
]));
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 1.14 给字符串新增方法实现功能
给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:
console.log( 'world'.addPrefix('hello') ) 控制台会输出helloworld
解答:
String.prototype.addPrefix = function(str){
return str + this;
}
console.log( 'world'.addPrefix('hello') )
1
2
3
4
5
2
3
4
5
# 1.15 找出字符串出现最多次数的字符以及次数
var str = 'aaabbbbbccddddddddddx';
var obj = {};
for(var i=0;i<str.length;i++){
var char = str.charAt(i);
if( obj[char] ){
obj[char]++;
}else{
obj[char] = 1;
}
}
console.log( obj );
//统计出来最大值
var max = 0;
for( var key in obj ){
if( max < obj[key] ){
max = obj[key];
}
}
//拿最大值去对比
for( var key in obj ){
if( obj[key] == max ){
console.log('最多的字符是'+key);
console.log('出现的次数是'+max);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1.16 new操作符具体做了什么
1. 创建了一个空的对象
2. 将空对象的原型,指向于构造函数的原型
3. 将空对象作为构造函数的上下文(改变this指向)
4. 对构造函数有返回值的处理判断
1
2
3
4
2
3
4
function Fun( age,name ){
this.age = age;
this.name = name;
}
function create( fn , ...args ){
//1. 创建了一个空的对象
var obj = {}; //var obj = Object.create({})
//2. 将空对象的原型,指向于构造函数的原型
Object.setPrototypeOf(obj,fn.prototype);
//3. 将空对象作为构造函数的上下文(改变this指向)
var result = fn.apply(obj,args);
//4. 对构造函数有返回值的处理判断
return result instanceof Object ? result : obj;
}
console.log( create(Fun,18,'张三') )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1.17 闭包
1. 闭包是什么
闭包是一个函数加上到创建函数的作用域的连接,闭包“关闭”了函数的自由变量。
函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包
2. 闭包可以解决什么问题【闭包的优点】
2.1 内部函数可以访问到外部函数的局部变量
2.2 闭包可以解决的问题
var lis = document.getElementsByTagName('li');
for(var i=0;i<lis.length;i++){
(function(i){
lis[i].onclick = function(){
alert(i);
}
})(i)
}
3. 闭包的缺点
3.1 变量会驻留在内存中,造成内存损耗问题。
解决:把闭包的函数设置为null
3.2 内存泄漏【ie】 ==> 可说可不说,如果说一定要提到ie
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.18 原型链
1. 原型可以解决什么问题
对象共享属性和共享方法
2. 谁有原型
函数拥有:prototype
对象拥有:__proto__
3. 对象查找属性或者方法的顺序
先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找
4. 原型链
4.1 是什么?:就是把原型串联起来
4.2 原型链的最顶端是null
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 1.19 JS继承有哪些方式
# 方式一:ES6
class Parent{
constructor(){
this.age = 18;
}
}
class Child extends Parent{
constructor(){
super();
this.name = '张三';
}
}
let o1 = new Child();
console.log( o1,o1.name,o1.age );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 方式二:原型链继承
function Parent(){
this.age = 20;
}
function Child(){
this.name = '张三'
}
Child.prototype = new Parent();
let o2 = new Child();
console.log( o2,o2.name,o2.age );
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 方式三:借用构造函数继承
function Parent(){
this.age = 22;
}
function Child(){
this.name = '张三'
Parent.call(this);
}
let o3 = new Child();
console.log( o3,o3.name,o3.age );
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 方式四:组合式继承
function Parent(){
this.age = 100;
}
function Child(){
Parent.call(this);
this.name = '张三'
}
Child.prototype = new Parent();
let o4 = new Child();
console.log( o4,o4.name,o4.age );
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 1.20 说一下call、apply、bind区别
# 共同点:功能一致
可以改变this指向
语法: 函数.call()、函数.apply()、函数.bind()
1
2
3
2
3
# 区别:
1. call、apply可以立即执行。bind不会立即执行,因为bind返回的是一个函数需要加入()执行。
2. 参数不同:apply第二个参数是数组。call和bind有多个参数需要挨个写。
1
2
2
# 场景:
1. 用apply的情况
var arr1 = [1,2,4,5,7,3,321];
console.log( Math.max.apply(null,arr1) )
2. 用bind的情况
var btn = document.getElementById('btn');
var h1s = document.getElementById('h1s');
btn.onclick = function(){
console.log( this.id );
}.bind(h1s)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 1.21 sort背后原理是什么?
V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,数量小于10的数组使用 InsertionSort,比10大的数组则使用 QuickSort。
之前的版本是:插入排序和快排,现在是冒泡
1
2
3
4
2
3
4
# 1.22 深拷贝和浅拷贝
共同点:复制
1. 浅拷贝:只复制引用,而未复制真正的值。
var arr1 = ['a','b','c','d'];
var arr2 = arr1;
var obj1 = {a:1,b:2}
var obj2 = Object.assign(obj1);
2. 深拷贝:是复制真正的值 (不同引用)
var obj3 = {
a:1,
b:2
}
var obj4 = JSON.parse(JSON.stringify( obj3 ));
//递归的形式
function copyObj( obj ){
if( Array.isArray(obj) ){
var newObj = [];
}else{
var newObj = {};
}
for( var key in obj ){
if( typeof obj[key] == 'object' ){
newObj[key] = copyObj(obj[key]);
}else{
newObj[key] = obj[key];
}
}
return newObj;
}
console.log( copyObj(obj5) );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 1.23 localStorage、sessionStorage、cookie的区别
公共点:在客户端存放数据
区别:
1. 数据存放有效期
sessionStorage : 仅在当前浏览器窗口关闭之前有效。【关闭浏览器就没了】
localStorage : 始终有效,窗口或者浏览器关闭也一直保存,所以叫持久化存储。
cookie : 只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效。
2. localStorage、sessionStorage不可以设置过期时间
cookie 有过期时间,可以设置过期(把时间调整到之前的时间,就过期了)
3. 存储大小的限制
cookie存储量不能超过4k
localStorage、sessionStorage不能超过5M
****根据不同的浏览器存储的大小是不同的。
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 1.24 JS有哪些内置对象?
String Boolean Number Array Object Function Math Date RegExp...
Math
abs() sqrt() max() min()
Data
new Data() getYear()
Array
String
concat() length slice() split()
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 1.25 操作数组的方法有哪些?
push() pop() sort() splice() unshift() shift() reverse() concat() join() map() filter()
ervery() some() reduce() isArray() findIndex()
哪些方法会改变原数组?
push() pop() unshift() shift() sort() reverse() splice()
1
2
3
4
2
3
4
# 1.26 JS对数据类的检测方式有哪些?
typeof()
instanceof()
constructor
Object.prototype.toString.call()
1
2
3
4
2
3
4
# 1.27 JS中关于this指向的问题
1. 全局对象中的this指向
指向的是window
2. 全局作用域或者普通函数中的this
指向全局window
3. this永远指向最后调用它的那个对象
在不是箭头函数的情况下
4. new 关键词改变了this的指向
5. apply,call,bind
可以改变this指向,不是箭头函数
6. 箭头函数中的this
它的指向在定义的时候就已经确定了
箭头函数它没有this,看外层是否有函数,有就是外层函数的this,没有就是window
7. 匿名函数中的this
永远指向了window,匿名函数的执行环境具有全局性,因此this指向window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.28 setTimeout最小执行时间是多少?
HTML5规定的内容:
setTimeout最小执行时间是4ms
setInterval最小执行时间是10ms
1
2
3
2
3
# 1.29 说一下事件循环
JS是一个单线程的脚本语言
主线程 执行栈 任务队列 宏任务 微任务
主线程先执行同步任务,然后才去执行任务队列里的任务,如果在执行宏任务之前有微任务,那么要先执行微任务
全部执行完之后等待主线程的调用,调用完之后再去任务队列中查看是否有异步任务,这样一个循环往复的过程就是事件循环!
1
2
3
4
2
3
4
# 1.30 浏览器的存储方式有哪些?
1.cookies
H5标准前的本地存储方式
兼容性好,请求头自带cookie
存储量小,资源浪费,使用麻烦(封装)
2.localstorage
H5加入的以键值对为标准的方式
操作方便,永久存储,兼容性较好
保存值的类型被限定,浏览器在隐私模式下不可读取,不能被爬虫
3.sessionstorage
当前页面关闭后就会立刻清理,会话级别的存储方式
4.indexedDB
H5标准的存储方式,,他是以键值对进行存储,可以快速读取,适合WEB场景
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 1.31 页面渲染的过程是怎样的?
DNS解析
建立TCP连接
发送HTTP请求
服务器处理请求
渲染页面
浏览器会获取HTML和CSS的资源,然后把HTML解析成DOM树
再把CSS解析成CSSOM
把DOM和CSSOM合并为渲染树
布局
把渲染树的每个节点渲染到屏幕上(绘制)
断开TCP连接
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 1.32 DOM树和渲染树有什么区别?
DOM树是和HTML标签一一对应的,包括head和隐藏元素
渲染树是不包含head和隐藏元素
1
2
2
# 1.33 了解过JWT吗?
JSON Web Token 通过JSON形式作为在web应用中的令牌,可以在各方之间安全的把信息作为JSON对象传输
信息传输、授权
JWT的认证流程
1.前端把账号密码发送给后端的接口
2.后端核对账号密码成功后,把用户id等其他信息作为JWT 负载,把它和头部分别进行base64编码拼接后签名,形成一个JWT(token)。
3.前端每日请求时都会把JWT放在HTTP请求头的Authorization字段内
4.后端检查是否存在,如果存在就验证JWT的有效性(签名是否正确,token是否过期)
5.验证通过后后端使用JWT中包含的用户信息进行其他的操作,并返回对应结果
简洁、包含性、因为Token是JSON加密的形式保存在客户端,所以JWT是跨语言的,原则上是任何web形式都支持。
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 1.34 HTTP协议规定的协议头和请求头有什么?
1.请求头信息:
Accept:浏览器告诉服务器所支持的数据类型
Host:浏览器告诉服务器我想访问服务器的哪台主机
Referer:浏览器告诉服务器我是从哪里来的(防盗链)
User-Agent:浏览器类型、版本信息
Date:浏览器告诉服务器我是什么时候访问的
Connection:连接方式
Cookie
X-Request-With:请求方式
2.响应头信息:
Location:这个就是告诉浏览器你要去找谁
Server:告诉浏览器服务器的类型
Content-Type:告诉浏览器返回的数据类型
Refresh:控制了的定时刷新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.34 说一下浏览器的缓存策略?
强缓存(本地缓存)、协商缓存(弱缓存)
强缓:不发起请求,直接使用缓存里的内容,浏览器把JS,CSS,image等存到内存中,下次用户访问直接从内存中取,提高性能
协缓:需要像后台发请求,通过判断来决定是否使用协商缓存,如果请求内容没有变化,则返回304,浏览器就用缓存里的内容
强缓存的触发:
HTTP1.0:时间戳响应标头
HTTP1.1:Cache-Control响应标头
协商缓存触发:
HTTP1.0:请求头:if-modified-since 响应头:last-modified
HTTP1.1:请求头:if-none-match 响应头:Etag
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 1.34 说一下什么是“同源策略”?
http:// www. aaa.com:8080/index/vue.js
协议 子域名 主域名 端口号 资源
同源策略是浏览器的核心,如果没有这个策略就会遭受网络攻击
主要指的就是协议+域名+端口号三者一致,若其中一个不一样则不是同源,会产生跨域
三个允许跨域加载资源的标签:img link script
跨域是可以发送请求,后端也会正常返回结果,只不过这个结果被浏览器拦截了!
JSONP
CORS
websocket
反向代理
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 1.35 防抖和节流是什么?
都是应对页面中频繁触发事件的优化方案
防抖:避免事件重复触发
使用场景:1.频繁和服务端交互 2.输入框的自动保存事件
节流:把频繁触发的事件减少,每隔一段时间执行
使用场景:scroll事件
1
2
3
4
5
2
3
4
5
# 1.36 解释一下什么是json?
JSON是一种纯字符串形式的数据,它本身不提供任何方法,适合在网络中进行传输
JSON数据存储在.json文件中,也可以把JSON数据以字符串的形式保存在数据库、Cookise中
JS提供了JSON.parse() JSON.stringify()
什么时候使用json:定义接口;序列化;生成token;配置文件package.json
1
2
3
4
2
3
4
# 1.37 有没有做过无感登录?
1.在相应其中拦截,判断token返回过期后,调用刷新token的接口
2.后端返回过期时间,前端判断token的过期时间,去调用刷新token的接口
3.写定时器,定时刷新token接口
流程:
1.登录成功后保存token 和 refresh_token
2.在响应拦截器中对401状态码引入刷新token的api方法调用
3.替换保存本地新的token
4.把错误对象里的token替换
5.再次发送未完成的请求
6.如果refresh_token过期了,判断是否过期,过期了就清楚所有token重新登录
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 1.38 大文件上传是怎么做的?
分片上传:
1.把需要上传的文件按照一定的规则,分割成相同大小的数据块
2.初始化一个分片上传任务,返回本次分片上传的唯一标识
3.按照一定的规则把各个数据块上传
4.发送完成后,服务端会判断数据上传的完整性,如果完整,那么就会把数据库合并成原始文件
断点续传:
服务端返回,从哪里开始 浏览器自己处理
1
2
3
4
5
6
7
2
3
4
5
6
7