Javascript知识点总结(1)

54 阅读4分钟
  1. Event Loop(事件循环/事件轮询)

event loop.png

    console.log("Hi")
    setTimeout(() => {
        console.log("Hello")
    }, 2000);
    console.log("yo")
    // Hi yo Hello

由于js是单线程,从上向下依次执行每行代码,同步代码直接推入call stack(执行栈)中执行,执行完成后从执行栈中移除,当遇到异步代码(定时器、ajax)时,异步代码的回调函数会在浏览器Web API进行实现,等到相应时机(定时器结束、网络请求返回)时,将回调放入事件队列。
所谓Event Loop机制,指的是一种内部循环,用来一轮又一轮地处理事件队列之中的事件,即执行对应的回调函数。

  1. Promise

    Promise时ES6中新增的一个类,目的是更加优雅地书写复杂的异步任务,避免了回调嵌套产生的“回调地狱”问题。
    Promise的三种状态:pedding(准备)、fullfilled(已完成)、rejected(已失败)

  2. 宏任务和微任务

    宏任务: setTimeout、setInterval、Ajax、DOM事件 (DOM渲染后执行)
    微任务: Promise、async\await (DOM渲染前执行)
    微任务执行时机要早于宏任务
    在Event loop中执行事件队列的回调完成,执行栈清空后,会尝试进行DOM渲染,而宏任务与微任务的执行时机就是在这一行为前后。

  3. 手写Promise

class MyPromise {
  state = "pending"; //初始状态
  value = undefined; //成功结果值
  reason = undefined; //失败原因
  resolveCallbacks = []; //pending状态,存储成功回调
  rejectCallbacks = []; //pending状态,存储失败回调

  //构造函数传入一个函数,函数有两个参数,分别为resolve、reject
  constructor(fn) {
    const resolveHandler = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        this.resolveCallbacks.forEach((fn) => fn(this.value));
      }
    };

    const rejectHandler = (reason) => {
      if (this.state === "pending") {
        this.state = "rejected";
        this.reason = reason;
        this.rejectCallbacks.forEach((fn) => fn(this.reason));
      }
    };

    try {
      fn(resolveHandler, rejectHandler);
    } catch (e) {
      rejectHandler(e);
    }
  }

  //then方法只在状态变为fulfilled或rejected执行,pending状态存入数组中
  then(fn1, fn2) {
    //对传入参数进行处理,必须为函数
    fn1 = typeof fn1 === "function" ? fn1 : (v) => v;
    fn2 = typeof fn2 === "function" ? fn2 : (e) => e;

    if (this.state === "pending") {
      return new MyPromise((resolve, reject) => {
        this.resolveCallbacks.push(() => {
          try {
            const newValue = fn1(this.value);
            resolve(newValue);
          } catch (e) {
            reject(e);
          }
        });

        this.rejectCallbacks.push(() => {
          try {
            const newReason = fn2(this.reason);
            reject(newReason);
          } catch (e) {
            reject(e);
          }
        });
      });
    }

    //成功或者失败状态 直接返回一个新的MyPromise对象
    if (this.state === "fulfilled") {
      return new MyPromise((resolve, reject) => {
        try {
          const newValue = fn1(this.value);
          resolve(newValue);
        } catch (e) {
          reject(e);
        }
      });
    }

    if (this.state === "rejected") {
      return new MyPromise((resolve, reject) => {
        try {
          const newReason = fn2(this.reason);
          reject(newReason);
        } catch (e) {
          reject(e);
        }
      });
    }
  }

  //catch是then方法的一个语法糖,只传入第二个参数
  catch(fn) {
    return this.then(null, fn);
  }
}

//实现静态方法
MyPromise.resolve = function (value) {
  return new MyPromise((resolve, reject) => resolve(value));
};

MyPromise.reject = function (reason) {
  return new MyPromise((resolve, reject) => reject(reason));
};

//只有当promiseList全部resolve才会返回fulfilled状态的MyPromise
MyPromise.all = function (promiseList = []) {
  return new MyPromise((resolve, reject) => {
    const length = promiseList.length;
    const result = [];
    let resolveCount = 0;
    promiseList.forEach((p) => {
      p.then((value) => {
        result.push(value);
        resolveCount++;
        if (resolveCount === length) {
          resolve(result);
        }
      }).catch((e) => {
        reject(e);
      });
    });
  });
};

//以第一个状态改变的MyPromise对象为准返回一个 fulfilled或rejected状态的MyPromise对象
MyPromise.race = function (promiseList = []) {
  return new MyPromise((resolve, reject) => {
    let resolved = false;

    promiseList.forEach((p) => {
      p.then((value) => {
        if (!resolved) {
          resolved = true;
          resolve(value);
        }
      }).catch((e) => {
        reject(e);
      });
    });
  });
};
  1. 数据类型

基本数据类型:number、string、symbol、boolean、undefinded
引用数据类型:object、null(特殊引用类型,指向空地址)、Function(特殊引用类型,不存储数据)
区别: 基本数据类型值存储在栈中,而引用数据类型只在栈中存储引用地址,实际数据存储在堆中 6. #### 递归深拷贝 意义: 引用类型赋值只能实现浅拷贝,即拷贝引用类型的内存地址,修改拷贝对象会造成原有对象改变,所以对于引用类型数据进行复制需要深拷贝

function deepClone(obj={}){
    //非数组和对象类型直接返回
    if(typeof obj !=="object" || obj==null){
        return obj
    }

    //初始克隆结果
    let result
    if(obj instanceof Array){
        result=[]
    }else{
        result={}
    }

    //遍历取值/赋值
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            const element = deepClone(obj[key]);
            result[key]=element
        }
    }

    return result
}
  1. 原型和原型链

class Animal {
    constructor(name){
        this.name=name
    }

    eat(){
        console.log(`${this.name} eat something`)
    }
}

class Cat extends Animal {
    constructor(name,age){
        super(name)
        this.age=age
    }

    mew(){
        console.log('mi mi')
    }
}

const cat=new Cat("mimi",18)
console.log(cat.__proto__===Cat.prototype) //true
console.log(cat.__proto__.__proto__===Animal.prototype) //true

原型.png __proto__: 隐式原型
prototype: 显式原型

获取实例的属性和方法,优先从自身查找,如果没有,则通过实例的__proto__指向类的显式原型prototype进行查找,如果类中没有则又通过类的显式原型的__proto__向继承父类prototype中查找,以此类推,直到顶层Object为止。
instanceof: 用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上

  1. 闭包

闭包是在编程中的一种概念,它指的是有权访问另一个函数作用域中的变量的函数
函数自由变量的查找,在函数定义的地方向上级作用域查找,而不是在函数执行的地方向上查找
函数内this的指向在函数执行的确定

//手写bind方法
Function.prototype.bind1 = function () {
  //将参数解析为数组
  const args = Array.from(arguments);
  
  //获取参数列表的第一项,即this
  const t = args.shift();

  //fn1.bind(...)中的fn1
  const self = this;

  //返回一个函数
  return () => {
    return self.apply(t, args);
  };
};

function fn(){
    console.log(this)  // {a:1}
}

const fn1=fn.bind1({a:1})
fn1()

闭包中的数据只在作用域中被修改和访问,可以隐藏数据

//闭包的应用 cache工具 隐藏数据
function createCache(){
    const data={}

    return {
        get:function(key){
            return data[key]
        },
        set:function(key,value){
            data[key]=value
        }
    }
}

const c=createCache()
c.set("name","jack")
const name=c.get("name")  //jack