最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 手撕JS(可能持续更新···)

    正文概述 掘金(wawoweb)   2021-01-17   485

    手撕JS(可能持续更新···)

      关于实现js中一些常见的方法属于面试中的常问问题,可能刚开始接触的时候会一筹莫展。知道和理解其中的原理能够在日常开发中更如鱼得水,面对面试也不成问题。另外,学会以目的(实现的功能)为导向一层一层反推,总结出实现的思路就能按照步骤直接实现或者曲线实现(整理不易记得点赞哈)。

    一、call的实现

      call() 方法:让call()中的对象调用当前对象所拥有的function。例如:test.call(obj,arg1,arg2,···) 等价于 obj.test(arg1,arg2,···);在手写实现call()方法前我们先进行分析,test调用call方法可以看作将test方法作为obj的一个属性(方法)调用,等obj.test()执行完毕后,再从obj属性上删除test方法:

    • 1、将函数设置为对象的属性;
    • 2、处理传入的参数;
    • 3、执行对象上设置的函数;
    • 4、删除对象上第一步设置的函数;

    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)
    

    打印的结果:

    a
    123
    hello
    { c: 'hello' }
    

    从结果可以看出:testthis.c输出为hello,说明thisobj;最后输出的obj也没有改变。

    二、apply的实现

      apply() 方法作用和call()完全一样,只是apply的参数第一个为需要指向的对象,第二个参数以数组形式传入。例如: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)
    

    打印的结果:

    world
    123
    hello
    { c: 'hello' }
    

    三、bind的实现

      bind方法:创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。例如:let fn = test.bind(obj,arg1,arg2,···); fn() 等价于 let fn = obj.test; fn(arg1,arg2,···);实现思路为:

    • 1、将函数设置为对象的属性;
    • 2、处理传入的参数;
    • 3、返回函数的定义/引用;
    • 4、外层执行接收的函数;
    • 5、删除对象上第一步设置的函数;

    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);
    

    打印的结果:

    a
    9527
    hello
    { c: 'hello' }
    

    四、Promise的实现

    1、分析Promise使用

      MDN中关Promise的定义如下:

    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);
      });
    

    Promise的使用分为三步:

    • 1、新建Promise实例,即通过new实现,同时接受一个函数参数,函数参数中接受resolve和reject两个形参(实质上也是函数);
    • 2、新建的Promise实例接受的函数参数中就是要执行的异步代码,并且用resolve和reject对异步结果进行调用输出;
    • 3、新建的Promise实例可以调用then和catch方法对异步结果进行接受和处理;

    上述新建实例代码可以转化为:

    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);
      });
    

      上述中的使用者就是then和catch,结合代码中的使用方式,简单来说就是Promise中执行异步操作,then和catch只会在异步执行完后才会接到返回结果继续执行!

    2、手撕Promise

      了解了Promise的定义和使用步骤后,接下来直接手撕Promise的实现,直接上实现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);
      });
    

    手撕JS(可能持续更新···)


    起源地下载网 » 手撕JS(可能持续更新···)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元