共计 11963 个字符,预计需要花费 30 分钟才能阅读完成。
TCP 粘包是怎么回事,如何解决?
默认状况下, TCP 连贯会启⽤提早传送算法 (Nagle 算法), 在数据发送之前缓存他们. 如果短时间有多个数据发送, 会缓冲到⼀起作⼀次发送 (缓冲⼤⼩⻅ socket.bufferSize), 这样能够缩小 IO 耗费提⾼性能.
如果是传输⽂件的话, 那么基本不⽤解决粘包的问题, 来⼀个包拼⼀个包就好了。然而如果是多条音讯, 或者是别的⽤途的数据那么就须要解决粘包.
上面看⼀个例⼦, 间断调⽤两次 send 别离发送两段数据 data1 和 data2, 在接收端有以下⼏种常⻅的状况:
A. 先接管到 data1, 而后接管到 data2 .
B. 先接管到 data1 的局部数据, 而后接管到 data1 余下的局部以及 data2 的全副.
C. 先接管到了 data1 的全副数据和 data2 的局部数据, 而后接管到了 data2 的余下的数据.
D. ⼀次性接管到了 data1 和 data2 的全副数据.
其中的 BCD 就是咱们常⻅的粘包的状况. ⽽对于解决粘包的问题, 常⻅的解决⽅案有:
- 屡次发送之前距离⼀个等待时间:只须要等上⼀段时间再进⾏下⼀次 send 就好, 适⽤于交互频率特地低的场景. 毛病也很显著, 对于⽐较频繁的场景⽽⾔传输效率切实太低,不过⼏乎不⽤做什么解决.
- 敞开 Nagle 算法:敞开 Nagle 算法, 在 Node.js 中你能够通过 socket.setNoDelay() ⽅法来敞开 Nagle 算法, 让每⼀次 send 都不缓冲间接发送。该⽅法⽐较适⽤于每次发送的数据都⽐较⼤ (但不是⽂件那么⼤), 并且频率不是特地⾼的场景。如果是每次发送的数据量⽐较⼩, 并且频率特地⾼的, 敞开 Nagle 纯属⾃废文治。另外, 该⽅法不适⽤于⽹络较差的状况, 因为 Nagle 算法是在服务端进⾏的包合并状况, 然而如果短时间内客户端的⽹络状况不好, 或者应⽤层因为某些起因不能及时将 TCP 的数据 recv, 就会造成多个包在客户端缓冲从⽽粘包的状况。(如果是在稳固的机房外部通信那么这个概率是⽐较⼩能够抉择疏忽的)
- 进⾏封包 / 拆包: 封包 / 拆包是⽬前业内常⻅的解决⽅案了。即给每个数据包在发送之前, 于其前 / 后放⼀些有特色的数据, 而后收到数据的时 候依据特色数据宰割进去各个数据包。
—- 问题知识点分割线 —-
元素的层叠程序
层叠程序,英文称作 stacking order,示意元素产生层叠时有着特定的垂直显示程序。上面是盒模型的层叠规定:
(1)背景和边框:建设以后层叠上下文元素的背景和边框。
(2)负的 z -index:以后层叠上下文中,z-index 属性值为负的元素。
(3)块级盒:文档流内非行内级非定位后辈元素。
(4)浮动盒:非定位浮动元素。
(5)行内盒:文档流外行内级非定位后辈元素。
(6)z-index:0:层叠级数为 0 的定位元素。
(7)正 z -index:z-index 属性值为正的定位元素。
留神: 当定位元素 z -index:auto,生成盒在以后层叠上下文中的层级为 0,不会建设新的层叠上下文,除非是根元素。
—- 问题知识点分割线 —-
call apply bind
题目形容: 手写 call apply bind 实现
实现代码如下:
Function.prototype.myCall = function (context, ...args) {if (!context || context === null) {context = window;}
// 发明惟一的 key 值 作为咱们结构的 context 外部办法名
let fn = Symbol();
context[fn] = this; //this 指向调用 call 的函数
// 执行函数并返回后果 相当于把本身作为传入的 context 的办法进行调用了
return context[fn](...args);
};
// apply 原理统一 只是第二个参数是传入的数组
Function.prototype.myApply = function (context, args) {if (!context || context === null) {context = window;}
// 发明惟一的 key 值 作为咱们结构的 context 外部办法名
let fn = Symbol();
context[fn] = this;
// 执行函数并返回后果
return context[fn](...args);
};
//bind 实现要简单一点 因为他思考的状况比拟多 还要波及到参数合并(相似函数柯里化)
Function.prototype.myBind = function (context, ...args) {if (!context || context === null) {context = window;}
// 发明惟一的 key 值 作为咱们结构的 context 外部办法名
let fn = Symbol();
context[fn] = this;
let _this = this;
// bind 状况要简单一点
const result = function (...innerArgs) {
// 第一种状况 : 若是将 bind 绑定之后的函数当作构造函数,通过 new 操作符应用,则不绑定传入的 this,而是将 this 指向实例化进去的对象
// 此时因为 new 操作符作用 this 指向 result 实例对象 而 result 又继承自传入的_this 依据原型链常识可得出以下论断
// this.__proto__ === result.prototype //this instanceof result =>true
// this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype; //this instanceof _this =>true
if (this instanceof _this === true) {
// 此时 this 指向指向 result 的实例 这时候不须要扭转 this 指向
this[fn] = _this;
this[fn](...[...args, ...innerArgs]); // 这里应用 es6 的办法让 bind 反对参数合并
} else {
// 如果只是作为一般函数调用 那就很简略了 间接扭转 this 指向为传入的 context
context[fn](...[...args, ...innerArgs]);
}
};
// 如果绑定的是构造函数 那么须要继承构造函数原型属性和办法
// 实现继承的形式: 应用 Object.create
result.prototype = Object.create(this.prototype);
return result;
};
// 用法如下
// function Person(name, age) {// console.log(name); //'我是参数传进来的 name'
// console.log(age); //'我是参数传进来的 age'
// console.log(this); // 构造函数 this 指向实例对象
// }
// // 构造函数原型的办法
// Person.prototype.say = function() {// console.log(123);
// }
// let obj = {
// objName: '我是 obj 传进来的 name',
// objAge: '我是 obj 传进来的 age'
// }
// // 一般函数
// function normalFun(name, age) {// console.log(name); //'我是参数传进来的 name'
// console.log(age); //'我是参数传进来的 age'
// console.log(this); // 一般函数 this 指向绑定 bind 的第一个参数 也就是例子中的 obj
// console.log(this.objName); //'我是 obj 传进来的 name'
// console.log(this.objAge); //'我是 obj 传进来的 age'
// }
// 先测试作为结构函数调用
// let bindFun = Person.myBind(obj, '我是参数传进来的 name')
// let a = new bindFun('我是参数传进来的 age')
// a.say() //123
// 再测试作为一般函数调用
// let bindFun = normalFun.myBind(obj, '我是参数传进来的 name')
// bindFun('我是参数传进来的 age')
—- 问题知识点分割线 —-
常见的 HTTP 申请办法
- GET: 向服务器获取数据;
- POST:将实体提交到指定的资源,通常会造成服务器资源的批改;
- PUT:上传文件,更新数据;
- DELETE:删除服务器上的对象;
- HEAD:获取报文首部,与 GET 相比,不返回报文主体局部;
- OPTIONS:询问反对的申请办法,用来跨域申请;
- CONNECT:要求在与代理服务器通信时建设隧道,应用隧道进行 TCP 通信;
- TRACE: 回显服务器收到的申请,次要⽤于测试或诊断。
—- 问题知识点分割线 —-
字符串模板
function render(template, data) {const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
if (reg.test(template)) { // 判断模板里是否有模板字符串
const name = reg.exec(template)[1]; // 查找以后模板里第一个模板字符串的字段
template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
return render(template, data); // 递归的渲染并返回渲染后的构造
}
return template; // 如果模板没有模板字符串间接返回
}
测试:
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let person = {
name: '布兰',
age: 12
}
render(template, person); // 我是布兰,年龄 12,性别 undefined
—- 问题知识点分割线 —-
Chrome 关上一个页面须要启动多少过程?别离有哪些过程?
关上 1 个页面至多须要 1 个网络过程、1 个浏览器过程、1 个 GPU 过程以及 1 个渲染过程,共 4 个;最新的 Chrome 浏览器包含:1 个浏览器(Browser)主过程、1 个 GPU 过程、1 个网络(NetWork)过程、多个渲染过程和多个插件过程。
浏览器过程
:次要负责界面显示、用户交互、子过程治理,同时提供存储等性能。渲染过程
:外围工作是将 HTML、CSS 和 JavaScript 转换为用户能够与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该过程中,默认状况下,Chrome 会为每个 Tab 标签创立一个渲染过程。出于平安思考,渲染过程都是运行在沙箱模式下。GPU 过程
:其实,Chrome 刚开始公布的时候是没有 GPU 过程的。而 GPU 的应用初衷是为了实现 3D CSS 的成果,只是随后网页、Chrome 的 UI 界面都抉择采纳 GPU 来绘制,这使得 GPU 成为浏览器广泛的需要。最初,Chrome 在其多过程架构上也引入了 GPU 过程。网络过程
:次要负责页面的网络资源加载,之前是作为一个模块运行在浏览器过程外面的,直至最近才独立进去,成为一个独自的过程。插件过程
:次要是负责插件的运行,因插件易解体,所以须要通过插件过程来隔离,以保障插件过程解体不会对浏览器和页面造成影响。
—- 问题知识点分割线 —-
懒加载的特点
- 缩小无用资源的加载:应用懒加载显著缩小了服务器的压力和流量,同时也减小了浏览器的累赘。
- 晋升用户体验: 如果同时加载较多图片,可能须要期待的工夫较长,这样影响了用户体验,而应用懒加载就能大大的进步用户体验。
- 避免加载过多图片而影响其余资源文件的加载:会影响网站利用的失常应用。
—- 问题知识点分割线 —-
Nginx 的概念及其工作原理
Nginx 是一款轻量级的 Web 服务器,也能够用于反向代理、负载平衡和 HTTP 缓存等。Nginx 应用异步事件驱动的办法来解决申请,是一款面向性能设计的 HTTP 服务器。
传统的 Web 服务器如 Apache 是 process-based 模型的,而 Nginx 是基于 event-driven 模型的。正是这个次要的区别带给了 Nginx 在性能上的劣势。
Nginx 架构的最顶层是一个 master process,这个 master process 用于产生其余的 worker process,这一点和 Apache 十分像,然而 Nginx 的 worker process 能够同时解决大量的 HTTP 申请,而每个 Apache process 只能解决一个。
—- 问题知识点分割线 —-
::before 和 :after 的双冒号和单冒号有什么区别?
(1)冒号 (:
) 用于 CSS3
伪类,双冒号 (::
) 用于 CSS3
伪元素。
(2)::before
就是以一个子元素的存在,定义在元素主体内容之前的一个伪元素。并不存在于 dom
之中,只存在在页面之中。
留神: :before
和 :after
这两个伪元素,是在 CSS2.1
里新呈现的。起初,伪元素的前缀应用的是单冒号语法,但随着 Web
的进化,在 CSS3
的标准里,伪元素的语法被批改成应用双冒号,成为::before
、::after
。
—- 问题知识点分割线 —-
代码输入后果
var a = 10;
var obt = {
a: 20,
fn: function(){
var a = 30;
console.log(this.a)
}
}
obt.fn(); // 20
obt.fn.call(); // 10
(obt.fn)(); // 20
输入后果:20 10 20
解析:
- obt.fn(),fn 是由 obt 调用的,所以其 this 指向 obt 对象,会打印出 20;
- obt.fn.call(),这里 call 的参数啥都没写,就示意 null,咱们晓得如果 call 的参数为 undefined 或 null,那么 this 就会指向全局对象 this,所以会打印出 10;
- (obt.fn)(),这里给表达式加了括号,而括号的作用是扭转表达式的运算程序,而在这里加与不加括号并无影响;相当于 obt.fn(),所以会打印出 20;
—- 问题知识点分割线 —-
什么是原型什么是原型链?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script>
function Person () {} var person = new Person(); person.name = 'Kevin'; console.log(person.name) // Kevin
// prototype
function Person () {} Person.prototype.name = 'Kevin'; var person1 = new Person(); var person2 = new Person(); console.log(person1.name)// Kevin
console.log(person2.name)// Kevin
// __proto__
function Person () {} var person = new Person(); console.log(person.__proto__ === Person.prototype) // true
//constructor
function Person() {} console.log(Person === Person.prototype.constructor) // true
// 综上所述
function Person () {} var person = new Person() console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 顺便学习一下 ES5 得办法, 能够取得对象得原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
// 实例与原型
function Person () {} Person.prototype.name = 'Kevin'; var person = new Person(); person.name = 'Daisy'; console.log(person.name) // Daisy
delete person.name; console.log(person.name) // Kevin
// 原型得原型
var obj = new Object(); obj.name = 'Kevin', console.log(obj.name) //Kevin
// 原型链
console.log(Object.prototype.__proto__ === null) //true
// null 示意 "没用对象" 即该处不应该有值
// 补充
function Person() {} var person = new Person() console.log(person.constructor === Person) // true
// 当获取 person.constructor 时,其实 person 中并没有 constructor 属性, 当不能读取到 constructor 属性时, 会从 person 的原型
// 也就是 Person.prototype 中读取时, 正好原型中有该属性, 所以
person.constructor === Person.prototype.constructor
//__proto__
// 其次是__proto__,绝大部分浏览器都反对这个非标准的办法拜访原型,然而它并不存在于 Person.prototype 中,实际上,它
// 是来自与 Object.prototype, 与其说是一个属性,不如说是一个 getter/setter, 当应用 obj.__proto__时,能够了解成返回了
// Object.getPrototypeOf(obj)
总结:1、当一个对象查找属性和办法时会从本身查找, 如果查找不到则会通过__proto__指向被实例化的构造函数的 prototype 2、隐式原型也是一个对象, 是指向咱们构造函数的原型 3、除了最顶层的 Object 对象没有__proto_,其余所有的对象都有__proto__, 这是隐式原型 4、隐式原型__proto__的作用是让对象通过它来始终往上查找属性或办法,直到找到最顶层的 Object 的__proto__属性,它的值是 null, 这个查找的过程就是原型链
</script>
</html>
—- 问题知识点分割线 —-
并发与并行的区别?
- 并发是宏观概念,我别离有工作 A 和工作 B,在一段时间内通过工作间的切换实现了这两个工作,这种状况就能够称之为并发。
- 并行是宏观概念,假如 CPU 中存在两个外围,那么我就能够同时实现工作 A、B。同时实现多个工作的状况就能够称之为并行。
—- 问题知识点分割线 —-
Unicode、UTF-8、UTF-16、UTF-32 的区别?
(1)Unicode
在说 Unicode
之前须要先理解一下 ASCII
码:ASCII 码(American Standard Code for Information Interchange
)称为美国规范信息替换码。
- 它是基于拉丁字母的一套电脑编码零碎。
- 它定义了一个用于代表常见字符的字典。
- 它蕴含了 ”A-Z”(蕴含大小写),数据 ”0-9″ 以及一些常见的符号。
- 它是专门为英语而设计的,有 128 个编码,对其余语言无能为力
ASCII
码能够示意的编码无限,要想示意其余语言的编码,还是要应用 Unicode
来示意,能够说 Unicode
是ASCII
的超集。
Unicode
全称 Unicode Translation Format
,又叫做对立码、万国码、繁多码。Unicode
是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了对立并且惟一的二进制编码,以满足跨语言、跨平台进行文本转换、解决的要求。
Unicode
的实现形式(也就是编码方式)有很多种,常见的是 UTF-8、UTF-16、UTF-32 和USC-2。
(2)UTF-8
UTF-8
是应用最宽泛的 Unicode
编码方式,它是一种可变长的编码方式,能够是 1—4 个字节不等,它能够齐全兼容 ASCII
码的 128 个字符。
留神: UTF-8
是一种编码方式,Unicode
是一个字符汇合。
UTF-8
的编码规定:
- 对于 单字节 的符号,字节的第一位为 0,前面的 7 位为这个字符的
Unicode
编码,因而对于英文字母,它的Unicode
编码和ACSII
编码一样。 - 对于 n 字节 的符号,第一个字节的前 n 位都是 1,第 n + 1 位设为 0,前面字节的前两位一律设为 10,剩下的没有提及的二进制位,全副为这个符号的
Unicode
码。
来看一下具体的 Unicode
编号范畴与对应的 UTF-8
二进制格局:
编码范畴(编号对应的十进制数) | 二进制格局 |
---|---|
0x00—0x7F(0-127) | 0xxxxxxx |
0x80—0x7FF(128-2047) | 110xxxxx 10xxxxxx |
0x800—0xFFFF(2048-65535) | 1110xxxx 10xxxxxx 10xxxxxx |
0x10000—0x10FFFF(65536 以上) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
那该如何通过具体的 Unicode
编码,进行具体的 UTF-8
编码呢?步骤如下:
- 找到该
Unicode
编码的所在的编号范畴,进而找到与之对应的二进制格局 - 将
Unicode
编码转换为二进制数(去掉最高位的 0) - 将二进制数从右往左一次填入二进制格局的
X
中,如果有X
未填,就设为 0
来看一个理论的例子:
“马 ”字的Unicode
编码是:0x9A6C
,整数编号是39532
(1)首选确定了该字符在第三个范畴内,它的格局是 1110xxxx 10xxxxxx 10xxxxxx
(2)39532 对应的二进制数为1001 1010 0110 1100
(3)将二进制数填入 X 中,后果是:11101001 10101001 10101100
(3)UTF-16
1. 立体的概念
在理解 UTF-16
之前,先看一下 立体 的概念:Unicode
编码中有很多很多的字符,它并不是一次性定义的,而是分区进行定义的,每个区寄存 65536(216)个字符,这称为一个 立体,目前总共有 17 个立体。
最后面的一个立体称为 根本立体 ,它的码点从0 — 216-1,写成 16 进制就是U+0000 — U+FFFF
,那剩下的 16 个立体就是 辅助立体,码点范畴是 U+10000—U+10FFFF
。
2. UTF-16 概念:
UTF-16
也是 Unicode
编码集的一种编码模式,把 Unicode
字符集的形象码位映射为 16 位长的整数(即码元)的序列,用于数据存储或传递。Unicode
字符的码位须要 1 个或者 2 个 16 位长的码元来示意,因而 UTF-16
也是用变长字节示意的。
3. UTF-16 编码规定:
- 编号在
U+0000—U+FFFF
的字符(罕用字符集),间接用两个字节示意。 - 编号在
U+10000—U+10FFFF
之间的字符,须要用四个字节示意。
4. 编码辨认
那么问题来了,当遇到两个字节时,怎么晓得是把它当做一个字符还是和前面的两个字节一起当做一个字符呢?
UTF-16
编码必定也思考到了这个问题,在根本立体内,从 U+D800 — U+DFFF
是一个空段,也就是说这个区间的码点不对应任何的字符,因而这些空段就能够用来映射辅助立体的字符。
辅助立体共有 220 个字符位,因而示意这些字符至多须要 20 个二进制位。UTF-16
将这 20 个二进制位分成两半,前 10 位映射在 U+D800 — U+DBFF
,称为 高位 (H),后 10 位映射在 U+DC00 — U+DFFF
,称为 低位(L)。这就相当于,将一个辅助立体的字符拆成了两个根本立体的字符来示意。
因而,当遇到两个字节时,发现它的码点在 U+D800 —U+DBFF
之间,就能够晓得,它前面的两个字节的码点应该在 U+DC00 — U+DFFF
之间,这四个字节必须放在一起进行解读。
5. 举例说明
以 “𡠀” 字为例,它的 Unicode
码点为 0x21800
,该码点超出了根本立体的范畴,因而须要用四个字节来示意,步骤如下:
- 首先计算超出局部的后果:
0x21800 - 0x10000
- 将下面的计算结果转为 20 位的二进制数,有余 20 位就在后面补 0,后果为:
0001000110 0000000000
- 将失去的两个 10 位二进制数别离对应到两个区间中
U+D800
对应的二进制数为1101100000000000
,将0001000110
填充在它的后 10 个二进制位,失去1101100001000110
,转成 16 进制数为0xD846
。同理,低位为0xDC00
,所以这个字的UTF-16
编码为0xD846 0xDC00
(4)UTF-32
UTF-32
就是字符所对应编号的整数二进制模式,每个字符占四个字节,这个是间接进行转换的。该编码方式占用的贮存空间较多,所以应用较少。
比方“马”字的 Unicode 编号是:U+9A6C
,整数编号是39532
,间接转化为二进制:1001 1010 0110 1100
,这就是它的 UTF-32 编码。
(5)总结
Unicode、UTF-8、UTF-16、UTF-32 有什么区别?
Unicode
是编码字符集(字符集),而UTF-8
、UTF-16
、UTF-32
是字符集编码(编码规定);UTF-16
应用变长码元序列的编码方式,相较于定长码元序列的UTF-32
算法更简单,甚至比同样是变长码元序列的UTF-8
也更为简单,因为其引入了独特的 代理对 这样的代理机制;UTF-8
须要判断每个字节中的结尾标记信息,所以如果某个字节在传送过程中出错了,就会导致前面的字节也会解析出错;而UTF-16
不会判断结尾标记,即便错也只会错一个字符,所以容错能力教强;- 如果字符内容全副英文或英文与其余文字混合,但英文占绝大部分,那么用
UTF-8
就比UTF-16
节俭了很多空间;而如果字符内容全副是中文这样相似的字符或者混合字符中中文占绝大多数,那么UTF-16
就占优势了,能够节俭很多空间;
—- 问题知识点分割线 —-
CSS 预处理器 / 后处理器是什么?为什么要应用它们?
预处理器, 如:less
,sass
,stylus
,用来预编译 sass
或者 less
,减少了css
代码的复用性。层级,mixin
,变量,循环,函数等对编写以及开发 UI 组件都极为不便。
后处理器, 如:postCss
,通常是在实现的样式表中依据 css
标准解决 css
,让其更加无效。目前最常做的是给css
属性增加浏览器公有前缀,实现跨浏览器兼容性的问题。
css
预处理器为 css
减少一些编程个性,无需思考浏览器的兼容问题,能够在 CSS
中应用变量,简略的逻辑程序,函数等在编程语言中的一些根本的性能,能够让 css
更加的简洁,减少适应性以及可读性,可维护性等。
其它 css
预处理器语言:Sass(Scss)
, Less
, Stylus
, Turbine
, Swithch css
, CSS Cacheer
, DT Css
。
应用起因:
- 构造清晰,便于扩大
- 能够很不便的屏蔽浏览器公有语法的差别
- 能够轻松实现多重继承
- 完满的兼容了
CSS
代码,能够利用到老我的项目中
—- 问题知识点分割线 —-
闭包产生的实质
以后环境中存在指向父级作用域的援用
—- 问题知识点分割线 —-
Sass、Less 是什么?为什么要应用他们?
他们都是 CSS 预处理器,是 CSS 上的一种形象层。他们是一种非凡的语法 / 语言编译成 CSS。例如 Less 是一种动静款式语言,将 CSS 赋予了动静语言的个性,如变量,继承,运算,函数,LESS 既能够在客户端上运行 (反对 IE 6+, Webkit, Firefox),也能够在服务端运行 (借助 Node.js)。
为什么要应用它们?
- 构造清晰,便于扩大。能够不便地屏蔽浏览器公有语法差别。封装对浏览器语法差别的反复解决,缩小无意义的机械劳动。
- 能够轻松实现多重继承。齐全兼容 CSS 代码,能够不便地利用到老我的项目中。LESS 只是在 CSS 语法上做了扩大,所以老的 CSS 代码也能够与 LESS 代码一起编译。
—- 问题知识点分割线 —-
代码输入后果
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
输入后果如下:
1
Promise {<fulfilled>: undefined}
Promise.resolve 办法的参数如果是一个原始值,或者是一个不具备 then 办法的对象,则 Promise.resolve 办法返回一个新的 Promise 对象,状态为 resolved,Promise.resolve 办法的参数,会同时传给回调函数。
then 办法承受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为 then(null),这就会导致前一个 Promise 的后果会传递上面。
—- 问题知识点分割线 —-
Promise 是什么,解决了什么,之前怎么实现的
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更正当和更弱小。解决来之前在申请中回调申请产生的回调天堂,使得当初的代码更加正当更加优雅,也更加容易定位查找问题。