前端基础知识整合(面筋)

455 阅读20分钟

1.HTML5离线存储

manifest文件是简单的文本文件,它告知浏览器被缓存的内容(以及不缓存的内容),支持manifest的浏览器,会将按照manifest文件的规则,将文件保存在本地,从而在没有网络链接的情况下,也能访问页面。

2.html中使用方式

<html manifest="demo.appcache">

<body>
The content of the document......
</body>

</html>

3.manifest文件的编写规范

CACHE MANIFEST
# 注释:需要缓存的文件,无论在线与否,均从缓存里读取
CACHE:
chched.js
cached.css

# 注释:不缓存的文件,无论缓存中存在与否,均从新获取
NETWORK:
uncached.js
uncached.css

# 注释:获取不到资源时的备选路径,如index.html访问失败,则返回404页面
FALLBACK:
index.html 404.html

4.localstorge cookie sessionstorge 区别

  1. localstorge浏览器本地存储单页面关闭时也不会被销毁
  2. sessionstorge只在页面会话期有效页面关闭时数据销毁
  3. cookie关闭时也不会被销毁但是内容存储一般为4kb左右一般存用户的登录信息

5.css优先级算法

行内样式 > 嵌入样式表 > 外链样式表

  1. !important > id > class > tag
  2. !import权重最高

6.请解释一下为什么需要清除浮动?清除浮动的方式 (参考29.BFC的知识)

清除浮动是为了清除使用浮动元素产生的影响。浮动的元素,高度会塌陷,而高度的塌陷使我们页面后面的布局不能正常显示。

  1. clear:both
  2. 浮动元素的父级div定义伪类:after
  3. 创建BFC

7.VUE.js的双向绑定原理

通过数据劫持结合发布者-订阅者模式的方式来实现的双向绑定
在getter中初始化需要被订阅的对象
在setter中如果数据变化,通知所有订阅者 数据劫持Object.defineProperty()方法
Object.defineProperty(obj, prop, descriptor):直接在一个对象上定义一个新属性,或者修改一个新属性,并返回这个对象

  1. 通过实现一个observer,实质是Object.defineProperty()方法设置get、set的来监听属性的变化,dep.notify()通知dep订阅器监听vue实例中的所有属性变化

  2. 消息订阅器Dep容器,订阅器Dep主要负责收集订阅者(收集被监听的对象)(ps:Dep=[watcher1,watcher2,watcher3.....])

  3. 订阅者Watcher在初始化的时候需要将自己添加进订阅器Dep中(只要在订阅者Watcher初始化的时候才需要添加订阅者到dep中)

  4. Compile编译解析Dom
    a. 解析模板指令,并替换模板数据,初始化视图
    b. 将模板指令对应的节点绑定对应的更新函数,初始化相应的订阅器

双向绑定流程:

  compile编译解析DOM节点 遍历节点匹配v-model指令绑定更新函数(callback()就是innerHTML)找到订阅者,
  --->然后watcher方法中向dep添加watcher
  compile-->初始化视图层
  首先Observer监听所有属性,当通过视图层(如:input改变绑定属性值)或模型层改变某个属性的时候,通知----->Dep(订阅器)  
  --->遍历Dep触发每个watcher(订阅者)的updatpe方法比较oldVal!== newVal 当不相等时说明这个属性已经改变了
  --->然后通过传入watcher的callback(dom.innerHTML)来渲染页面  
  

8.get 与 post 的区别

1.get:把参数包含在URL中。
2.post:参数通过request-body传递参数,没有参数长度限制
3.get请求参数有长度限制而post没有长度限制
4.参数类型:get请求只接受ASCII字符,而post没有限制
5.GET请求只能进行url(encodeURIComponent)编码,而POST支持多种编码方式

9.Xss 与 Csrf 网络安全知识

Xss:跨站脚本攻击, 脚本注入

原理:攻击者的输入没有经过后台的过滤直接进入到数据库,最终显示给来访的用户。
防御方法:避免 XSS 的方法之一主要是对用户输入的内容进行过滤

CSRF:跨站请求伪造

原理:受害者登录受信任网站A,并在本地生成Cookie,然后在访问危险网站B。
防御:给每个表单加入随机 Token进行验证,这样B页面无法获取A页面的 Token 导致请求验证失败,从而防止了 CSRF。

post 请求中 Content-Type

Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析

application/x-www-form-urlencoded

application/json

text/xml

multipart/form-data

10.请求状态码

  • 200 请求成功
  • 301 请求资源已被永久的移到了新URL
  • 302 请求资源临时转移到新URl
  • 304 请求的资源未修改
  • 401 请求要求用户身份认证
  • 403 服务器拒绝执行请求
  • 404 请求地址不存在
  • 500 服务器错误
  • 501 服务器不支持请求的功能

11.事件循环

异步任务队列执行顺序是:先执行微任务,所有的微任务执行完在执行宏任务(宏任务微任务都属于异步事件)

宏任务
1.setInterval
2.setTimeout
3.I/O

微任务
1.promise
2.process.nextTick

11.SEO原理

爬行和抓取

搜索引擎派出一个能够在网上发现新网页并抓文件的程序,这个程序通常称之为蜘蛛(Spider)。搜索引擎从已知的数据库出发,就像正常用户的浏览器一样访问这些网页并抓取文件。搜索引擎通过这些爬虫去爬互联网上的外链,从这个网站爬到另一个网站,去跟踪网页中的链接,访问更多的网页,这个过程就叫爬行。这些新的网址会被存入数据库等待搜索。所以跟踪网页链接是搜索引擎蜘蛛(Spider)发现新网址的最基本的方法,所以反向链接成为搜索引擎优化的最基本因素之一。搜索引擎抓取的页面文件与用户浏览器得到的完全一样,抓取的文件存入数据库。

建立索引

蜘蛛抓取的页面文件分解、分析,并以巨大表格的形式存入数据库,这个过程即是索引(index).在索引数据库中,网页文字内容,关键词出现的位置、字体、颜色、加粗、斜体等相关信息都有相应记录。

搜索词处理

用户在搜索引擎界面输入关键词,单击“搜索”按钮后,搜索引擎程序即对搜索词进行处理,如中文特有的分词处理,去除停止词,判断是否需要启动整合搜索,判断是否有拼写错误或错别字等情况。搜索词的处理必须十分快速。

排序

对搜索词处理后,搜索引擎程序便开始工作,从索引数据库中找出所有包含搜索词的网页,并且根据排名算法计算出哪些网页应该排在前面,然后按照一定格式返回到“搜索”页面。 再好的搜索引擎也无法与人相比,这就是为什么网站要进行搜索引擎优化。没有SEO的帮助,搜索引擎常常并不能正确的返回最相关、最权威、最有用的信息。

防止SEO降权

  1. 添加优质外链
  2. 页面结构清晰
  3. 减少对主题标题的更改
  4. 设置meta标签keyword等属性

12.common.js规范与es6模块的区别

common.js规范 (node中使用多)

  1. module.exports 与 exports
    exports = module.exports = {}
    exports 和 module.exports在一个node执行一个文件时,会给这个文件内生成一个 exports和module对象,
    而module又有一个exports属性。他们之间的关系如下图,都指向一块{}内存区域。

      //exports实质是module.exports的引用所以在导出node模块不可以 exports='....' 这样就覆盖了引用
      let a = 100;
    
      console.log(module.exports); //能打印出结果为:{}
      console.log(exports); //能打印出结果为:{}
    
      exports.a = 200; //这里辛苦劳作帮 module.exports 的内容给改成 {a : 200}
    
      exports = '指向其他内存区'; //这里把exports的指向指走
    
      //test.js
    
      var a = require('/utils');
      console.log(a) // 打印为 {a : 200}  
    
    

其实require()引用的是module.exports的指向的内存块内容,而exports只是module.exports的引用,辅助后者添加内容用的。

用白话讲就是,exports只辅助module.exports操作内存中的数据,辛辛苦苦各种操作数据完,累得要死,结果到最后真正被require出去的内容还是module.exports的,真是好苦逼啊。

ES6模块

  1. export 与 export.default 的区别
  //a.js
  var a = 1;
  function b (){...}
  export {a}
  export {b}
  export default a
  //b.js
  import { a , b } , x  from './a.js'
  console.log(a) //1
  console.log(b) //function....
  console.log(x) //1

export 在文件中可以有多个,但是export.default(将模块按系统默认导出)只能有一个
export 可以直接导出表达式而export.default不可以(export var a=1)

1.export与export default均可用于导出常量、函数、文件、模块等
2.在一个文件或模块中,export、import可以有多个,export default仅有一个
3.通过export方式导出,在导入时要加{ },export default则不需要
4.export能直接导出变量表达式,export default不行。

```
  'use strict'
  //导出变量
  export const a = '100';  

  //导出方法
  export const dogSay = function(){ 
      console.log('wang wang');
  }

  //导出方法第二种
  function catSay(){
    console.log('miao miao'); 
  }
  export { catSay };

  //export default导出
  const m = 100;
  export default m; 
  //export defult const m = 100;// 这里不能写这种格式。

```  

在webpack中存在使用require引用export defalut 或者export

在 babel5 时代,大部分人在用 require 去引用 es6 输出的 default,只是把 default 输出看作是一个模块的默认输出,所以 babel5 对这个逻辑做了 hack,如果一个 es6 模块只有一个 default 输出,那么在转换成 commonjs 的时候也一起赋值给 module.exports,即整个导出对象被赋值了 default 所对应的值.
这样就不需要加 default,require('./a.js') 的值就是想要的 default值。

  ```
    //a.js
    var a = 1
    export defalut a
    // b.js
    require('./a.js').default;
  ```

还有一个很重要的问题,一旦 a.js 文件里又添加了一个具名的输出,那么引入方就会出麻烦。

```
  // a.js

  export default 123;

  export const a = 123; // 新增

  // b.js 

  var foo = require('./a.js');
  console.log(foo)
  // 由之前的 输出 123
  // 变成 { default: 123, a: 123 }

```

所以 babel6 去掉了这个hack,这是个正确的决定,升级 babel6 后产生的不兼容问题 可以通过引入 babel-plugin-add-module-exports 解决。

13.bind的实现原理

  Function.prototype.mybind=function(context){
    let self = this 
    //获取到除了需要绑定的上下文以外的所有参数
    //let args = Array.prototype.slice.call(arguments,1)
    return function (){
      //额外参数执行bind后返回的方法传入参数
      let fnArgs = Array.prototype.slice.call(arguments)
      self.apply(context,fnArgs)
    }
  }
  function a(age){
    console.log(age)
    console.log(this.name)
  }
  var b={
    name:'sheyang'
  }
  a.mybind(b)('x')
function Animal(a,b){
  console.log(a+b)
&emsp;&emsp;&emsp;this.species = "动物";
}
function Cat(name,color){
      console.log(arguments)
&emsp;&emsp;&emsp;&emsp;Animal.apply(this, arguments);
&emsp;&emsp;&emsp;&emsp;this.name = name;
&emsp;&emsp;&emsp;&emsp;this.color = color;
      console.log(name)
      console.log(color)
&emsp;&emsp;}
&emsp;&emsp;var cat1 = new Cat("大毛","黄色");
  console.log(cat1)
&emsp;&emsp;alert(cat1.species); // 动物
//执行了两次apply 一次在构造函数继承的时候,将上下文绑定至cat作用域,第二次是new
//时将上下文绑定至new中新的创建一个对象上


14.实现apply与call

  //1.apply思路:将要改变this指向的方法挂到目标this上执行并返回
  Function.prototype.myapply(context){
    if(typeof this! == 'function'){
      console.log('Not a function')
      return 
    }
    context = context || window
    context.fn = this
    let result
    //appy 传入的第二个参数为类数组所以arguments的length为2 第一个为上下文对象,第二个参数为传入///函数的参数
    if(arguments[1]){
      result = context.fn(...arguments[1])
    }else{
      result = context.fn()
    }
    delet context.fn
    return result
  }
  //2.call思路:将要改变this指向的方法挂到目标this上执行并返回
  Function.prototype.mycall(context){
    if(typeof this ! == 'function'){
      console.log('not a function ')
      return 
    }
    context = context || window
    context.fn = this
    let args = [...arguments].slice(1)
    let result = context.fn(...args)
    delete context.fn
    return result
  }


15.继承

  //1.构造函数继承
  //缺点:子类实例获取不到父类原型对象上的属性
  function superType(){
    this.a=[1,2,3,4]
  }
  superType.prototype.b = '我是父类原型上的属性'
  function child(x,y){
    superType.apply(this,arguments)
    this.x = x
    this.y = y
  }
  var c = new child ('sheyang','....')
  var d = new child ('hehe','....')
  c.a.push(5)
  console.log(c)//{a:[1,2,3,4,5],x:'sheyang',y:'....'}
  console.log(d)//{a:[1,2,3,4],x:'hehe',y:'....'}
  console.log(c.b)//undefined 

  // 2. 原型链继承
  //缺点:由于父类上的属性是共享,子类的实例修改父类上的引用类型属性时,会影响其他子类实例
  function superType(){
    this.a=[1,2,3,4]
  }
  superType.prototype.b = '我是父类原型上的属性'
  function child(x,y){
    this.x = x
    this.y = y
  }
  child.prototype = new superType()
  var c = new child ('sheyang','....')
  var d = new child ('hehe','....')
  c.a.push(5)
  console.log(c)//{a:[1,2,3,4,5],x:'sheyang',y:'....'}
  console.log(d)//{a:[1,2,3,4,5],x:'hehe',y:'....'}
  console.log(c.b)//我是父类原型上的属性 

  //组合继承
  //优点:解决了构造函数继承和原型链继承的缺点
  //缺点:父类构造函数被执行两次1次是在子类中的apply第二次是在child.prototype = new superType()
  //new中的也有apply。这样就会有子类的实例中已经存在了父类的属性,且子类的原型对象上也存在父类属性。
  //会导致内存浪费
  function superType(){ 
    this.a=[1,2,3,4]
  }
  superType.prototype.b = '我是父类原型上的属性'
  function child(x,y){
    superType.apply(this,arguments)
    this.x = x
    this.y = y
  }
  child.prototype = new superType()
  var c = new child ('sheyang','....')
  var d = new child ('hehe','....')
  c.a.push(5)
  console.log(c)//{a:[1,2,3,4,5],x:'sheyang',y:'....'}
  console.log(d)//{a:[1,2,3,4],x:'hehe',y:'....'}
  console.log(c.b)//我是父类原型上的属性 

  //寄生组合式继承
  //特点:在组合式继承的基础上减少一次父类构造函数的执行,解决内存浪费问题
  function superType(){ 
    this.a=[1,2,3,4]
  }
  superType.prototype.b = '我是父类原型上的属性'
  function child(x,y){
    superType.apply(this,arguments)
    this.x = x
    this.y = y
  }
  //由于这一部会将子类原型上的constructor重写,所以我们要修复这一个问题.
  child.prototype = superType.prototype
  child.prototype.constructor = child
  var c = new child ('sheyang','....')
  var d = new child ('hehe','....')
  c.a.push(5)
  console.log(c)//{a:[1,2,3,4,5],x:'sheyang',y:'....'}
  console.log(d)//{a:[1,2,3,4],x:'hehe',y:'....'}
  console.log(c.b)//我是父类原型上的属性 

  //原型式继承(es6:Object.create())
  //缺点无法传递参数
  //对引用类型修改时,属性会被篡改
  function create(obj){
    var f = function(){}
    f.prototype = obj
    return new f()
    //new 操作符是实际原理
    //new f() = { 
    // 	var obj={} 
    // 	obj.__proto__ = f.prototype //建立原型链
    // 	var result = f.apply(obj,arguments)
    //	return result
    // }
  }
  var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
  };
  var anotherPerson = create(person);
  anotherPerson.name = "Greg";
  anotherPerson.friends.push("Rob");

  var yetAnotherPerson = create(person);
  yetAnotherPerson.name = "Linda";
  yetAnotherPerson.friends.push("Barbie");
  console.log(anotherPerson)
  console.log(yetAnotherPerson)

16.es6 class 类

  class Point {
    constructor() {
      // ...
    }

    toString() {   //实例方法 挂载在原型对象上的是实例方法
      // ...
    }

    toValue() {  //实例方法
      // ...
    }
    static sayName(){  //静态方法

    }
  }

  // 等同于

  Point.prototype = {
    constructor() {},
    toString() {},
    toValue() {},
  };
  //构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。
  point.prototype.constructor === point //true


  //this的指向
  class Logger(){
    printName(name='haha'){
      this.print(`Hello${name}`)
    }
    print(text){
      console.log(text)
    }
  }
  const logger = new Logger()
  const {printName} = logger
  printName() // TypeError: Cannot read property 'print' of undefined
  //class类中的this默认指向实例对象。 如果静态方法包含this关键字,这个this指的是类,而不是实例。 
  // 但是当单独出来用的时候this指向运行环境
  //printName(name='haha'){
  //   `use strict`
  //    this.print(`Hello${name}`)
  //  }
  //
  //在用于class 用的严格模式所以this指向undefined 本应该指向window

  //单独时候时解决方法将this绑定到实例对象上
  //constructor(){
  //  this.printName = this.printName.bind(this)
  //}

  //或者使用箭头函数

17.class 继承

```
class ColorPoint extends Point {   //父类可以看做是原型对象
  constructor(x, y, color) {
    this.x = x //ReferenceError
    super(x, y); // 调用父类的constructor(x, y) super() === Point.prototype.constructor.call(this)。
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}
//上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
//注意点
// 1.子类中必须在constructor 中调用super方法  得到与父类同样的实例属性和方法,再加上子类自己的实例属性和方法。
// 2.只有调用super之后,才可以使用this关键字,否则会报错 
//3. super() 只能在子类的构造函数中用 A.  第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

// 第一种情况super() 表示A.prototype.constructor.call(this)。 调用父类构造函数
// 第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
//super 表示指向父类的原型对象所以定义在父类实例上的方法或属性,是无法通过super调用的
//super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()内部的this指向子类B的实例,
//Object.getPrototypeOf(child) === father// ture 方法可以用来判断一个类是否继承了另一个类。


class A {
  constructor() {
    this.x = 1;
  }
  static print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  static m() {
    super.print();
  }
}

B.x = 3;
B.m() // 3

//另外,在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例
//在子类的静态方法中调用super对象指向的是父类,普通方法中指向的父类的原型对象

```

18.从输入 URL 到页面展示 这过程发生了什么

  1. 判断输入内容是搜索内容,还是请求的 URL。
  2. 查找缓存查找DNS缓存找出对应的ip地址然后建立tcp链接(参考22:三次握手四次挥手)
  3. 下载js、css、html、解析html生成dom树
  4. 解析css生成cssom
  5. 渲染进程开始渲染dom树

19.浏览器渲染(重排、重绘、合成)

20.如何系统的优化性能

加载阶段

  1. 优化原则就是减少关键资源个数 (关键资源:js、html、css)
  2. 降低关键资源大小。
  3. 降低关键资源的 RTT 次数 (RTT 就是这里的往返时延。它是网络中一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延)

预解析线程会快速扫描 HTML 数据中的关键资源,一旦扫描到了,会立马发起请求,JavaScript 和 CSS 是同时发起请求的,所以它们的请求是重叠的,那么计算它们的 RTT 时,只需要计算体积最大的那个数据就可以了。

交互阶段

  1. 减少 JavaScript 脚本执行时间
  2. 避免强制同步布局 (强制同步布局:JavaScript 强制将计算样式和布局操作提前到当前的任务中,JavaScript 还需要强制让渲染引擎默认执行一次布局操作。我们把这个操作称为强制同步布局。比如计算高度)
  3. 避免布局抖动 (在一段js中,反复执行布局操作就是布局抖动)
  4. 合理利用 CSS 合成动画
  5. 避免频繁的垃圾回收

21.刷新页面,JS 请求一般会有哪些地方有缓存处理?

  1. DNS缓存
  2. cdn缓存
  3. 浏览器缓存
  4. 服务器缓存

22.一次完整的 HTTP 事务是怎么一个过程

  1. 建立连接的三次握手
    a. 客户端发起建立连接请求。
    b. 服务端回复,收到,统一建立连接。
    c. 客户端回复收到服务端的回复
  2. 断开连接的四次挥手
    a. 客户端发起断开连接请求
    b. 服务端回复收到
    c. 服务端发起断开连接请求
    d. 客户端回复收到

23.css有几种加载方式

1. link标签引入
2. @import 导入样式
3. 行内样式
4. 内嵌样式

页面导入时,使用 link 和 @import 有什么区别?

1. link 是html标签 不仅仅可以引入css文件,@import 是css引入的语法,只能引入css
2. link 是页面在加载时同时加载,@import 等页面被加载完成时在加载css

24.src与href 的区别

src:代表资源、一般是用于下载文件  
href:表示与远程连接建立锚点,一般用于连接跳转  

25.函数声明提升

  1. 只有函数声明格式的函数才会存在函数声明提前,比如函数表达式,构造函数,都不存在函数声明提前。
    函数创建的三种写法:
    a.函数声明:function fun(a){console.log(a)};(只有这个家伙存在函数声明提前)

    b.函数表达式:var fun = function(a){console.log(a)};

    c.构造函数:var fun = new Function("a",console.log(a));

26.闭包

闭包就是由函数创造的一个词法作用域,里面创建的变量被引用后,可以在这个词法环境之外自由使用。 闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。

27.VUEX 与 Redux 的区别

  1. VUEX与Redux都是基于flux架构 (view、store、action) Vuex 流程:
    view --->dispatch--->actions--->commit --->mutations --->state ---> view
    Redux流程: view--->dispatch --->ations--->reduce --->state--->view

28.函数科里化

  function multiplication (){
    var args = [].slice.apply(arguments)
    var multer = function (){
      var _mult=function(){
        args.push(...arguments)
        return _mult
      }
      //利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
      _mult.toString=function(){
        return args.reduce(function (a,b){
          return a*b
        })
      }
      return _mult
    }
    return multer()
  }
  var a = multiplication(1,2,3)(4)(2)

29.什么是BFC?什么条件下会触发?应用场景有哪些?

  1. BFC块级格式上下文:
      浮动元素和绝对定位元素,非块级盒子的块级容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不为"visiable"的块级盒子,都会为他们的内容创建新的BFC(Block Fromatting Context, 即块级格式上下文)。
  2. 什么条件下会触发:
     根元素()
     浮动元素(元素的 float 不是 none)
     绝对定位元素(元素的 position 为 absolute 或 fixed)
     行内块元素(元素的 display 为 inline-block)
     表格单元格(元素的 display为 table-cell,HTML表格单元格默认为该值)
     表格标题(元素的 display 为 table-caption,HTML表格标题默认为该值)
     匿名表格单元格元素(元素的 display为 table、table-row、 table-row-group、table-header-group、table-footer-group(分别是HTML table、row、tbody、thead、tfoot的默认属性)或 inline-table)
     overflow 值不为 visible 的块元素 -弹性元素(display为 flex 或 inline-flex元素的直接子元素)
     网格元素(display为 grid 或 inline-grid 元素的直接子元素) 等等
  1. BFC渲染规则:
     1.BFC垂直方向边距重叠
     2.BFC的区域不会与浮动元素的box重叠
     3.BFC是一个独立的容器,外面的元素不会影响里面的元素
     4.计算BFC高度的时候浮动元素也会参与计算(清楚浮动导致的高度塌陷)

30.导致前端页面白屏的原因

      原因:
         scirpt脚本会阻塞Dom渲染
      解决方案:
         使用<script>元素的async或defer属性。  

31.promise 知识

  1. Promise的状态一经改变就不能再改变。
  2. then和.catch都会返回一个新的Promise。
  3. catch不管被连接到哪里,都能捕获上层未捕捉过的错误。
  4. 在Promise中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如return 2会被包装为return Promise.resolve(2)。
  5. Promise 的 .then 或者 .catch 可以被调用多次, 但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch的时候都会直接拿到该值
  6. then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获
  7. then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
  8. then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传
  9. then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch是.then第二个参数的简便写法
  10. finally方法也是返回一个Promise,他在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数。

32.this永远指向最后调用它的那个对象

33.最后附上一篇大佬面筋文章