一、Promise

1.概述

  1. 当把一个任务交给 promise 的时候,它的状态就是Pending,任务完成之后状态变为Resolved,没有完成失败就变成了Rejected。
  2. 一旦状态发生改变,那么就不能更改状态了。

2. then(解决回调地狱)

  1. then 的默认返回值是Promise。
  2. 成功的结果通过 resolve 传递给 then ,下一次的异步可以在下一个 then 中进行可以一直链式调用。
  3. then 的回调函数中如果写了 return,该值当做 Promise 的成功结果传递给下一个 then
  4. 如果在 then 中单独返回一个 promise ,那么下一次 then 的成功结果来自于上一个then 中成功回调里面返回的 promise 的 resolve 的结果。
  5. then 的中断:可以通过回调中的 reject / throw new Error 进行中断。

3. await async

  1. await 用来修饰 promise。
  2. async 用来修饰 await 就近的函数。
  3. async 修饰的函数返回值为 promise。
  4. 被 async 修饰的函数一定可以被 await 修饰。
  5. 捕获 async 和 await 中的错误。
    • trycatch 捕获
    • 原型方法 catch 捕获:then().catch()

4. 静态方法

  1. Promise.all()
    • 可以获取多个 promise 处理异步的结果,返回值是 promise
    • 可以调用 .then(),当all 数组中所有promise 都resolve()之后才执行 then ,只要有一个reject()那么就中断。
    • all 的then 返回的结果就是对应的 promise 返回的数据
    • 内部通过reduce默认遍历数组,通过当前项调用 then来判断当前操作成功与否,失败就走 reject ,成功就走 resolve 传递给下一次 then,一旦有一个项走了reject那就操作就中断了。
  2. Promise.allsettled()
    • 也可以获取多个 promise 异步处理的结果,不管 resolve 还是 reject 都会执行 then ,结果返回一个数组,每个对象通过 status 记录对应 promise 的状态( fulfilled / rejected )
    • 通过 then 中成功还是失败来控制 结果对象中的 status的值,成功就为fulfilled 失败就为rejected 并且会返回一个 reason 的字段。
  3. Promise.race()
    • 和 all 一样,只返回第一个结果(成功或者失败)。
  4. Promise.any()
    • 返回多个 promise 处理结果中第一个成功的结果。

5. Generator 函数

概念:可以将函数的控制权交出(将函数的执行暂停),也可以利用其更方便的控制异步

格式:用 * 来标记 generator 函数,用 yield 来进行函数暂停(交出控制权)。

function *fn(){ 
  //yield 交出控制权
	yield console.log(1)
     yield console.log(2)
     return 3
}

逐步调用

const gen = fn()
gen.next()

向外传递数据 :调用next()会返回一个对象,对象里有两个属性,一个是value(每次的返回值),一个是done(函数是否执行结束)

function *fn(){ 
	yield 1
     yield 2
     return 3
}
const gen = fn()
console.log(gen.next())//{value:1,done:false}
console.log(gen.next())//{value:2,done:false}
console.log(gen.next())//{value:3,done:true}

处理异步:配合 promise 处理

function *fn() {
   yield new Promise(resolve => {
    setTimeout(() => {
      console.log(1)
      resolve()
    }, 2000)
  })

  yield new Promise(resolve => {
    setTimeout(() => {
      console.log(2)
      resolve()
    }, 1000)
  })

  yield new Promise(resolve => {
    setTimeout(() => {
      console.log(3)
      resolve()
    }, 3000)
  })
}

const gen = fn()
//调用(回调地狱)
gen.next().value.then(() => {
  gen.next().value.then(() => {
    gen.next()
  })
})

//利用co 递归
function co(gen) {
  //res 的value属性里是一个  promise 对象
  const res = gen.next()
  //如果函数执行完毕就结束递归
  if (res.done) return
  //拿到 promise 的 成功回调的返回数据
  res.value.then(() => {
    co(gen)
  })
}

${\textcolor{red}{async、await和generator的区别:}}$

async、await和generator的区别

6. Promise A+ 规范

规定了Promise开发设计时的规则。

7. 手写 Promise 源码


// 定义三个状态
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
function resolvePromise(x, promise2, resolve, reject) {
  //判断x === promise, 抛出类型错误
  if (x === promise2) {
    console.log('======')
    return reject(new TypeError('类型错误'))
  }
  // 允许状态改变一次
  let called = false

  try {
    //判断x是否包含then属性,thenable
    if (typeof x === 'object' && x !== null) {
      const then = x.then
      if (typeof then === 'function') {
        // console.log(typeof then)
        x.then(
          x,
          v => {
            if (called) return
            called = true
            resolvePromise(v, promise2, resolve, reject)
          },
          r => {
            if (called) return
            called = true
            reject(r)
          }
        )
      } else {
        if (called) return
        called = true
        resolve(x)
      }
    } else {
      if (called) return
      called = true
      resolve(x)
    }
  } catch (e) {
    if (called) return
    called = true
    reject(e)
  }
}
class Promise {
  constructor(exectuor) {
    try {
      //捕获执行器错误
      exectuor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }

  status = PENDING
  value = null
  reason = null
  //存储失败和成功的回调
  onFullFilledCallbacks = []
  onRejectedCallbacks = []

  static all(args) {
    return new Promise((resolve, reject) => {
      args.reduce((prev, curr, i, arr) => {
        if (curr instanceof Promise) {
          curr.then(
            v => {
              prev[i] = v
              if (prev.length === arr.length) {
                resolve(prev)
              }
            },
            r => {
              reject(r)
            }
          )
        } else {
          prev[i] = curr
        }
        return prev
      }, [])
    })
  }
  static resolve(v) {
    if (v instanceof Promise) return v
    return new Promise((resolve, reject) => resolve(v))
  }
  static reject(r) {
    return new Promise((resolve, reject) => {
      reject(r)
    })
  }
  static allSettled(args) {
    return new Promise((resolve, reject) => {
      function addData(prev, index, value) {
        prev[index] = value
        if (prev.length === args.length) {
          resolve(prev)
        }
      }
      args.reduce((prev, curr, index, arr) => {
        if (curr instanceof Promise) {
          curr.then(
            res => {
              addData(prev, index, {
                value: res,
                status: 'fulfilled'
              })
            },
            r => {
              addData(prev, index, {
                reason: r,
                status: 'rejected'
              })
            }
          )
        } else {
          addData(prev, index, {
            reason: curr,
            status: 'fulfilled'
          })
        }
      })
    })
  }

  static race(args) {
    return new Promise((resolve, reject) => {
      args.forEach(item => {
        if (item instanceof Promise) {
          item.then(
            v => {
              resolve(v)
            },
            r => {
              reject(r)
            }
          )
        } else {
          resolve(item)
        }
      })
    })
  }

  finally(cb) {
    return this.then(
      v => {
        return Promise.resolve(cb()).then(() => v)
      },
      r => {
        return Promise.resolve(cb()).then(() => {
          throw r
        })
      }
    )
  }

  resolve = v => {
    //只有状态为pending才执行
    if (this.status === PENDING) {
      this.status = RESOLVED
      this.value = v
      console.log(this.onFullFilledCallbacks)
      this.onFullFilledCallbacks.forEach(c => c())
    }
  }

  reject = r => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = r
      this.onRejectedCallbacks.forEach(c => c())
    }
  }

  then(onFullFilled, onRejected) {
    debugger
    //onFullFilled onRejected类型判断
    if (typeof onFullFilled !== 'function') onFullFilled = v => v
    if (typeof onFullFilled !== 'function') {
      onRejected = r => {
        throw r
      }
    }
    const promise2 = new Promise((resolve, reject) => {
      if (this.status === RESOLVED) {
        // Promise为微任务,所以放到微任务队列执行
        queueMicrotask(() => {
          try {
            const x = onFullFilled(this.value)
            resolvePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }

      if (this.status === REJECTED) {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }

      if (this.status === PENDING) {
        //如果状态为pending,则执行方法放入数组中,等待resolve或reject时候执行
        this.onFullFilledCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              const x = onFullFilled(this.value)
              resolvePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })

        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              const x = onRejected(this.reason)
              resolvePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }
}

Promise.deferred = function() {
  var result = {}
  result.promise = new Promise(function(resolve, reject) {
    result.resolve = resolve
    result.reject = reject
  })

  return result
}

module.exports = Promise

resolvePromise() 源码分析

// 判断 x 是不是对象 并且不是null
// x 是 return new Promise((resolve, reject) => {resolve(2)})
if (typeof x === 'object' && x !== null) {
  // 如果是对象 x是否有then方法
  const then = x.then
  if (typeof then === 'function') {
    // console.log(typeof then)
    // 手动返回的promise.then
    x.then(
      v => {
        if (called) return
        called = true
        // 有可能手动返回的promise中的resolve还有可能继续返回Promise
        resolvePromise(v, promise2, resolve, reject)
      },
      r => {
        if (called) return
        called = true
        reject(r)
      }
    )
  } else {
    if (called) return
    called = true
    // 默认返回promise的resolve
    // 既然走到了这里 x 不是promise 那x就应该携带到下一个then中
    resolve(x)
  }
} else {
  if (called) return
  called = true
  // x 不是一个对象,直接调用resolve(x) 传递到下一个then中
  resolve(x)
}

8. 源码分析

  1. 初始化
  1. 执行构造函数
  1. 调用resolve()
  1. 调用 then() 执行异步
  1. then 中手动返回 promise

二、同步异步

1. 概念

2. 执行流程

3. 宏任务

由宿主环境发起的异步任务。

setTimeOut、setInterval、script代码块

4. 微任务

由 javascript 自身发起的任务。

.then()、catch()、MutationObserver

5. 宏任务、微任务执行顺序

async function async1() {
  console.log('async1 start')
  await async2()
  // await后面的代码可以理解为promise.then(function(){ 回调执行的 })
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')
setTimeout(function() {
  console.log('setTimeout')
}, 0)

async1()

console.log('script end')

// script start、async1 start、async2、script end、async1 end'、setTimeout

	const async1 = async () => {
        console.log('async1')
        setTimeout(() => {
          console.log('timer1')
        }, 2000)
       
        await new Promise((resolve) => {
          console.log('promise1')
          // resolve()
        })
        console.log('async1 end')
        return 'async1 success'
      }
      console.log('script start')
      async1().then((res) => console.log(res))
      console.log('script end')
      Promise.resolve(1)
        .then(2)
        .then(Promise.resolve(3))
        .catch(4)
        .then((res) => console.log(res))
      setTimeout(() => {
        console.log('timer2')
      }, 1000)
      
 //script start、async1、promise1、script end、1、timer2、timer1

三 、闭包

1. 概念

2. 原理

3. 作用

4. 缺点

四、垃圾回收机制

1. 概念

js 内存是自动进行分配和回收,内存在不使用的时候会被垃圾回收自动回收,当然还有一些情况下,内存无法回收,比如(使用闭包不当的话),这时候就需要手动的回收。

2. 生命周期

主要为三个阶段

3. 算法

五、数据类型以及类型检测

1. 数据类型

2. 数据类型的检测

type of

正常检测出:Number、Boolean、String、Function、Undefined、Symbol、BigInt

Null 和 Undefined 的区别?

instanceof

用于检测构造函数的 prototype 属性是否出现在实例对象的原型链上。

//判断实例对象是否可以根据原型链找到对应的构造函数的原型对象
      const _instanceof = (target, Fn) => {
        // 补全代码
        let targetProto = target.__proto__;
        let fnProto = Fn.prototype;
        console.log(targetProto, fnProto);
        while (true) {
          if (targetProto == fnProto) {
            return true;
          }
          if (targetProto == null) {
            return false;
          }
          targetProto = targetProto.__proto__;
        }
      };
      let arr = new Array();
      str = "";
      // console.log(arr);
      console.dir(arr);
      console.log(arr instanceof Array, _instanceof(arr, Array));
      console.log(str instanceof Array, _instanceof(str, Array));
      console.log(arr instanceof Object, _instanceof(arr, Object));

toString

它是 Object 的原型方法,调用它会返回当前对象的[[Class]]。会返回[[object xxx]],xxx就是数据类型。

constructor

constructor本质是对函数本身的引用。可以检测由字面量方式创建出来的数据类型。因为我们可以通过实例的原型找到它的 constructor ,而constructor就是构造函数本事 ,字面量方式的本质就是创建了一个这个对象的实例。因此就可以判断这个实例是不是由该构造类型创建的。

isArray()

用来检测是否为数组

const arr = [];
Array.isArray(arr);//true

3. Set和Map数据结构

特点:成员的值唯一,没有重复的值,可以用来数组去重,[…new Set(array)],可以接受数组或者伪数组作为参数。

声明:new Set()

${\textcolor{red}{方法}}$:添加成员:add()

​ 判断成员是否存在:has()

删除成员返回布尔:delete() 清除set:clear()

​ 遍历set:foreach()

特点:首先成员只能是对象,其次WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

let ws = new WeakSet();
      ws.add({
        name: "张三",
        age: 18,
      });
      ws.add({
        name: "李四",
        age: 20,
      });
      console.log(ws);

img

声明:new Map()

${\textcolor{red}{方法}}$:添加成员:set(key,value)

判断成员是否存在:has(key) 获取成员的值:get(key) 删除成员 :delete(key) 返回Map结构成员:size 清除所有成员,没有返回值:clear()
let o = {
        userm: "wer",
      };
      let map = new Map();
      map.set(o, "age");
      console.log(map.get(o));
      console.log(map.has(o));
      //   map.delete(o);
      console.log(map);

img

WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。

// WeakMap 可以使用 set 方法添加成员
const wm1 = new WeakMap();
const key = {foo: 1};
wm1.set(key, 2);
wm1.get(key) // 2

// WeakMap 也可以接受一个数组,
// 作为构造函数的参数
const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
wm2.get(k2) // "bar"

4. String 和 toString 的区别

  1. String 可以将所有的数据都转化为字符串,但是 toString() 不能对null和undefined进行转换
<script>
    var str1 = false.toString();
    var str2 = String(flase);
    console.log(str1,typeof str1 );//false string
    console.log(str2,typeof str2 );//false string
</script>
  1. 使用 toString() 对数字进行转换的时候可以通过在括号中添加数字,来将数字转换为相应的进制,但是使用 String() 不能进行进制转换。
<script>
    var str1 = String(undefined);
    console.log(str1,typeof str1);
    var str2 = undefined.toString();
    console.log(str2,typeof str2);
 </script>

六、继承

1. 原型链继承

      function Father(uname) {
        this.uanme = uname;
        this.some = "yiixe";
        this.arr = [1, 2, 3, 4];
      }
      //把函数的方法定义到 原型 上
      Father.prototype.work = function () {
        console.log("工作");
      };

      function Son(uname) {
        this.uanme = uname;
      }
      Son.prototype = new Father();
      //这样 Son的原型里就没有了构造函数
      Son.prototype.constructor = Son;
      console.log(Son.prototype); //Father

      Son.prototype.study = function () {
        console.log("学习");
      };

      let father = new Father("父类1");
      let father2 = new Father("父类2");
      father.work(); //工作
      father2.work(); //工作

      let son = new Son("子类");
      let son1 = new Son("子类1");
      son.study(); //学习
      console.log(son.arr); //[1,2,3,4]
      son.arr.push(5);
      console.log(son1.arr); //[1,2,3,4,5]
      console.log(father.arr); //[1,2,3,4]
      son1.arr = [1, 2];
      console.log(son1.arr); //[1,2]
      console.log(father.arr); //[1,2,3,4]

缺点:

2. 借用构造函数继承

function Father(uname, age) {
        this.uname = uname;
        this.age = age;
        this.work = function () {
          console.log("工作");
        };
      }

      function Son(uname, age) {
        Father.call(this); //让父类型的this指向子类
        this.uname = uname;
        this.age = age;
        this.study = function () {
          console.log("学习");
        };
      }

      let father = new Father("父类", 30);
      console.log(father.age);
      father.work();// 工作
      father.__proto__.work1 = function () {
        console.log("再工作");
      };
      father.work1();// 再工作
      console.log("===========");
      let son = new Son("儿子", 18);
      console.log(son.uname, son.age);// 男,18
      son.gender = "";
      console.log(son.gender);// 男
      son.study();// 学习
      son.work();// 工作 
      son.work1();//work1 not  defined

缺点:

3. 组合式继承

// 创建人的类
function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype.say = function() {
  console.log('说话了')
}
function Dog(name, age) {
  Person.call(this, name, age)
}

// 此方式不推荐 子原型和父原型公用同一个原型
// Dog.prototype = Person.prototype
Dog.prototype = new Person()

const d1 = new Dog('大黄', 3)
const d2 = new Dog('小黄', 2)

缺点:

4. 寄生式组合继承

通过 Object.create(数据),可以创建一个对象,该对象的原型指向参数对象。

// 创建人的类
function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype.say = function() {
  console.log('说话了')
}
function Dog(name, age) {
  Person.call(this, name, age)
}

// 此方式不推荐 子原型和父原型公用同一个原型
// Dog.prototype = Person.prototype
Dog.prototype = Object.create(Person.prototype)

const d1 = new Dog('大黄', 3)
const d2 = new Dog('小黄', 2)

5. ES6类继承

class Person {
  say() {
    console.log('说话了')
  }
}

class Child extends Person {}

const child = new Child()
child.say()

七、深浅拷贝

1. 浅拷贝

  1. ` Object.assign({},obj)` 浅拷贝
  2. ... 扩展运算符

2. 深拷贝

  1. JSON实现深拷贝
  1. 因此可以在通过递归深拷贝的时候需要把对应丢失的数据类型重新new 之后再返回。

深浅拷贝的区别

八、new 的过程

九、ES6

1.let、const

  1. 具有块级作用域,在块级作用域之外访问不到。
  2. 不存在变量提升。
  3. 具有暂时性死区:只要块级作用域中声明了let或const,就不会受外界影响,如果在声明之前使用这些变量或常量 ,就会报错。
  4. 不允许重复声明。

十、字符串方法

1. substr 和 substring

subString3结果

十一、数组方法

十二、this指向

1. 函数模式调用

2. 对象模式调用

3. 箭头函数

4. 构造函数

5. 修改this指向

call apply bind

localstoragesessionstoreage 为 ==5~20MB

获取 cookie 获取的是整个字符串,无法精确或缺到某一个值,需要引入第三方库。

image-20220914202604446