Hand tear JS (may continue to be updated···)

Hand tear JS (may continue to be updated···)

  Some common methods in js are frequently asked questions in interviews, and they may be at a loss when they first contact. Knowing and understanding the principles can be more comfortable in daily development, and face interviews are not a problem. In addition, learn to use the purpose (realized function) as the direction to reverse the layer by layer, and summarize the idea of ​​realization, which can be realized directly or in a curve according to the steps (it is not easy to remember the sorting 点赞).

1. Implementation of call

  call()Method: Let the object in call() call the function owned by the current object. For example test.call(obj,arg1,arg2,···) 等价于 obj.test(arg1,arg2,···):; Before handwriting the implementation of the call()method, we will analyze it first. The testcalling callmethod can be regarded as calling the testmethod as objan attribute (method). obj.test()After the execution is completed, the method objis deleted from the attribute test:

  • 1. Set the function as an attribute of the object;
  • 2. Process the incoming parameters;
  • 3. Execute the function set on the object;
  • 4. Delete the function set in the first step on the object;

myCall:

function test(a, b) {
  console.log(a);
  console.log(b);
  console.log(this.c);
}

let obj = {
  c: "hello",
};

//myCall
Function.prototype.myCall = function () {
  //声明传入上下文为传入的第一个参数,如果没有传参默认为global(node环境),如果是浏览器环境则为 window;
  let context = arguments[0] || global; 
  //将调用myCall方法函数(this)设置为 声明的传入上下文中的fn函数;
  context.fn = this;
  //对函数参数进行处理
  var args = [];
  for (let index = 0; index < arguments.length; index++) {
    index > 0 && args.push(arguments[index]);
  }
  //执行fn,也就是调用myCall方法的函数
  context.fn(...args);
    //执行完毕后删除传入上下文的fn,不改变原来的对象
  delete context.fn;
};

test.myCall(obj, "a", 123);
console.log(obj)

Printed result:

a
123
hello
{ c: 'hello' }

It can be seen from the result that the output testin the middle this.cis hello, the explanation thisis obj; the final output objhas not changed.

Second, the realization of apply

  apply()The function of the method is call()exactly the same, except applythat the first parameter is the object to be pointed to, and the second parameter is passed in as an array. For example test.apply(obj,[arg1,arg2,···]) 等价于 obj.test(arg1,arg2,···):;

myApply:

//myApply
Function.prototype.myApply = function(){
  let context = arguments[0] || global;
  context.fn = this;
  var args = arguments.length > 1 ? arguments[1] : [];
  context.fn(...args);
  delete context.fn;
}

test.myApply(obj, ["world", 123]);
console.log(obj)

Printed result:

world
123
hello
{ c: 'hello' }

Three, the realization of bind

  bindMethod: Create a new function, when called, set its this keyword to the provided value, when calling the new function, provide a given sequence of parameters before any provided. For example let fn = test.bind(obj,arg1,arg2,···); fn() 等价于 let fn = obj.test; fn(arg1,arg2,···):; The realization idea is:

  • 1. Set the function as an attribute of the object;
  • 2. Process the incoming parameters;
  • 3. The definition/reference of the return function;
  • 4. The outer layer executes the received function;
  • 5. Delete the function set in the first step on the object;

myBind:

Function.prototype.myBind = function(){
  //1.1、声明传入上下文为传入的第一个参数,如果没有传参默认为global(node环境),如果是浏览器环境则为 window;
  let context = arguments[0] || global;
  //1.2、将调用myBind方法函数(this)设置为 声明的传入上下文中的fn函数;
  context.fn = this;
  //2.1、对调用myBind的函数参数进行处理
  var args = [];
  for (let index = 0; index < arguments.length; index++) {
    index > 0 && args.push(arguments[index]);
  }
    //3、声明和定义函数变量F,用于返回给外层
  let F = function  (){
    //2.2、对再次传入的参数进行处理,追加到
    for (let index = 0; index < arguments.length; index++) {
      args.push(arguments[index]);
    }
    //4.2、执行实际的调用myBind方法函数
    context.fn(...args);
    //5、执行完毕后删除传入上下文的fn,不改变原来的对象
    delete context.fn;
  }
  return F;
}

var f = test.myBind(obj, "a")
//4.1、执行返回的函数
f(9527);

Printed result:

a
9527
hello
{ c: 'hello' }

Fourth, the realization of Promise

1. Analyze the use of Promise

  PromiseThe definition of MDN Zhongguan is as follows:

Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。<br/>一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。<br/>
  <br/>一个 Promise 必然处于以下几种状态之一:<br/>
    <br/>待定(pending): 初始状态,既没有被兑现,也没有被拒绝。<br/>
    <br/>已兑现(fulfilled): 意味着操作成功完成。<br/>
    <br/>已拒绝(rejected): 意味着操作失败。<br/>
  <br/>待定状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序就会被调用,因此在完成异步操作和绑定处理方法之间不会存在竞争状态<br/>

new Promise((resolve, reject) => {
  //异步操作
  //···
  //执行完后调用resolve和reject输出两种不同结果
  if (true) {
    resolve("res");
  } else {
    reject("err");
  }
})
  .then((res) => { //then接受resolve中的结果
    console.log(res);
  })
  .catch((err) => { //catch接受reject中的结果
    console.log(err);
  });

The use of Promise is divided into three steps:

  • 1. Create a new Promise instance, which is realized by new, and accept a function parameter at the same time, and the function parameter accepts two formal parameters of resolve and reject (essentially a function);
  • 2. The function parameters accepted by the newly created Promise instance are the asynchronous code to be executed, and use resolve and reject to call and output the asynchronous result;
  • 3. The newly created Promise instance can call the then and catch methods to accept and process asynchronous results;

The above new instance code can be transformed into:

function fn(resolve, reject) {
  //异步操作
  //···
  //执行完后调用resolve和reject输出两种不同结果
  if (true) {
    resolve("res");
  } else {
    reject("err");
  }
}

let p = new Promise(fn);

p.then((res) => { //then接受resolve中的结果
    console.log(res);
  })

p.catch((err) => { //catch接受reject中的结果
    console.log(err);
  });

  The user in the above is then和catch, combined with the usage in the code,简单来说就是Promise中执行异步操作,then和catch只会在异步执行完后才会接到返回结果继续执行!

2. Hand tear Promise

  After understanding the definition and usage steps of Promise, then directly tear up the realization of Promise and directly upload the code that realizes Promise ( 内涵大量注释,基本一句一解释,但是逻辑还是得第三部分来讲):

// 定义promise中的三种状态
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";

// 定义promise的类
class myPromise {
  //class的构造函数,接受新建实例时的参数:executor在promise中是一个函数
  constructor(executor) {
    //初始化该class中的初始状态
    this.status = STATUS_PENDING;
    //定义class中成功(res)和失败(err)时的变量值
    this.res = "";
    this.err = "";

    //promis异步中最重要的异步,定义成功和错误函数存储的数组,存放异步时还没有执行的操作
    this.onResCallbacks = [];
    this.onErrCallbacks = [];

    //定义该构造函数constructor定义域中的变量resolve
    let resolve = (res) => {
      // 首先判断该class中的状态,只有状态为pending时才能转化class转态为fulfilled或者rejected
      if (this.status === STATUS_PENDING) {
        //修改class的转态为fulfilled,也就表示不会转进行其他转态的转化了
        this.status = STATUS_FULFILLED;
        //将成功(resolve)状态下的值赋给class的成功返回res
        this.res = res;
        //此时状态由pending转为fulfilled,执行之前在then中存放的需要执行的异步操作,promise的then中参数res接受结果
        this.onResCallbacks.forEach((fn) => {
          fn();
        });
      }
    };

    //定义该构造函数constructor定义域中的变量reject
    let reject = (err) => {
      // 首先判断该class中的状态,只有状态为pending时才能转化class转态为fulfilled或者rejected
      if (this.status === STATUS_PENDING) {
        //修改class的转态为rejected,也就表示不会转进行其他转态的转化了
        this.status = STATUS_REJECTED;
        //将失败(reject)状态下的值赋给class的失败返回err
        this.err = err;
        //此时状态由pending转为rejected,执行之前在catch中存放的需要执行的异步操作,promise的catch中参数err接受结果
        this.onErrCallbacks.forEach((fn) => {
          fn();
        });
      }
    };

    //按照promise中的逻辑,在调用时就立即执行了,所以在手写的myPromise创建构造函数constructor时就执行executor
    try {
      //执行参入的函数,并将上述定义的resolve和reject作为参数传入
      executor(resolve, reject);
    } catch (err) {
      //报错时调用失败的状态函数
      reject(err);
    }
  }

  //在class中定义promise的成功状态接收函数then,按照promise逻辑,then中传入的一般都是一个函数
  then(onRes = () => {}) {
    //如果是异步的,此时在constructor中status的状态还没变成fulfilled,所以会跳过onRes调用,没有返回
    if (this.status === STATUS_FULFILLED) {
      onRes(this.res);
    }
    //但是我们将此时的异步放入数组存放
    if (this.status === STATUS_PENDING) {
      this.onResCallbacks.push(() => onRes(this.res));
    }
    //这步操作保证了then和catch能够在同级一起"."调起,当then上述操作完后,返回class实例,便可以接在后面继续调用catch
    return this;
  }

  //在class中定义promise的失败状态接收函数catch,按照promise逻辑,catch中传入的一般都是一个函数
  catch(onErr = () => {}) {
    //如果是异步的,此时在constructor中status的状态还没变成rejected,所以会跳过onErr调用,没有返回
    if (this.status === STATUS_REJECTED) {
      onErr(this.err);
    }
    //但是我们将此时的异步放入数组存放
    if (this.status === STATUS_PENDING) {
      this.onErrCallbacks.push(() => onErr(this.err));
    }
     //这步操作保证了then和catch能够在同级一起"."调起,当catch上述操作完后,返回class实例,便可以接在后面继续调用then
    return this;
  }
}

//调用自己手写的promise
new myPromise((resolve, reject) => {
  console.log("进入了手写的promise");
  //用setTimeOut模拟异步操作
  setTimeout(() => {
    if (false) {
      resolve("输出成功结果resolve");
    } else {
      reject("输出失败结果reject");
    }
  }, 2000); //按照js的特性,此时不会等待异步完成,直接调用then或者catch
})
  .then((res) => {
    console.log("then:", res);
  })
  .catch((err) => { //return this具体作用体现在这里
    console.log("catch:", err);
  });

Hand tear JS (may continue to be updated···)

Guess you like

Origin blog.51cto.com/15066867/2593547