js手写系列

1.手写new

      function myNew() {
        // 1.用new Object() 的方式新建了一个对象 obj
        const obj = new Object();
        // 2.取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
        const constructor = [].shift.call(arguments);
        // 3.将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性
        obj.__proto__ = constructor.prototype;
        // 4.使用 apply执行构造函数,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
        const res = constructor.apply(obj, arguments);
        // 5.因为构造函数可能有返回值, 且返回值只有引用类型才能生效 返回
        return typeof res === "object" ? res : obj;
      }
复制代码

2.手写call、apply

      Function.prototype.myCall = function (context) {
        // this 参数可以传 null,当为 null 的时候,视为指向 window
        context = context || window;
        // 将函数设为对象的属性
        context.fn = this;
        // 获取函数的参数
        // call 是 ES3 的方法,用 eval 方法拼成一个函数
        var args = [];
        for (var i = 1; i < arguments.length; i++) {
          args.push("arguments[" + i + "]");
        }
        // 执行函数 context.fn()
        // 这里 args 会自动调用 Array.toString() 这个方法。
        var result = eval("context.fn(" + args + ")");
        // 删除该函数
        delete context.fn;
        // 返回值
        return result;
      };
复制代码
      Function.prototype.myApply = function (context, arr) {
        // this 参数可以传 null,当为 null 的时候,视为指向 window
        context = context || window;
        // 将函数设为对象的属性
        context.fn = this;
        // 获取函数的参数
        arr = arr.length ? arr : [];
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
          args.push("arr[" + i + "]");
        }
        // 执行函数 context.fn()
        // 这里 args 会自动调用 Array.toString() 这个方法。
        var result = eval("context.fn(" + args + ")");
        // 删除该函数
        delete context.fn;
        // 返回值
        return result;
      };
复制代码

3、手写bind

      Function.prototype.myBind = function (context) {
        // 绑定的必须是函数
        if (typeof this !== "function") {
          throw new Error(
            "Function.prototype.bind - 尝试被绑定的对象是不可调用的"
          );
        }
        // 获取执行函数
        var self = this;
        // 获取myBind函数从第二个参数到最后一个参数
        var args = Array.prototype.slice.call(arguments, 1);
        // 空函数 用来中转
        var fNOP = function () {};

        // bind返回的函数也可以传入参数,因此要将两次的参数合并起来
        // bind返回的函数可以作为构造函数,此时bind绑定的this会失效,但传入的参数依然生效
        var fBound = function () {
          // 这个时候的arguments是指bind返回的函数传入的参数
          var bindArgs = Array.prototype.slice.call(arguments);
          // this instanceof fNOP 如果为true 则此时是将bind返回的函数当做构造函数使用,即 new fBound(), 将绑定函数的 this 指向该实例
          // this instanceof fNOP 如果为false, 则作为普通函数时,this 指向 window,将绑定函数的 this 指向 context
          // args.concat(bindArgs) 将参数拼接
          return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
        };
        // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
        // 这里不直接使用 fBound.prototype = this.prototype 原因是修改 fBound.prototype 的时候,也会直接修改绑定函数的 prototype
        // 这个时候,我们可以通过一个空函数来进行中转
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        // 返回
        return fBound;
      };
复制代码

4、手写Promise

    class MP {
      static PENDING = 'PENDING'; // 等待状态
      static FULFILED = 'FULFILED'; // 成功状态
      static REJECTED = 'REJECTED'; // 失败状态
      constructor(exectour) {
        this.value = null;
        this.status = MP.PENDING;
        // 还没有resolve或者reject的时候,在then的回调函数中将回调函数压入callbacks中
        this.callbacks = [];
        try {
          exectour(this.resolve.bind(this), this.rejected.bind(this));
        } catch (error) {
          this.rejected(error);
        }
      }
      resolve(value) {
        if (this.status !== MP.PENDING) return;
        this.status = MP.FULFILED;
        this.value = value;
        setTimeout(() => {
          // 异步执行
          this.callbacks.forEach((callback) => {
            callback.onFulfilled(value);
          });
        });
      }
      rejected(value) {
        if (this.status !== MP.PENDING) return;
        this.status = MP.REJECTED;
        this.value = value;
        setTimeout(() => {
          this.callbacks.forEach((callback) => {
            callback.onRejected(value);
          });
        });
      }
      then(onFulfilled, onRejected) {
        if (typeof onFulfilled !== 'function') {
          onFulfilled = () => this.value; // 值穿透
        }
        if (typeof onRejected !== 'function') {
          onRejected = () => this.value;
        }
        const promise = new MP((resolve, rejected) => {
          // 等待状态
          if (this.status === MP.PENDING) {
            this.callbacks.push({
              onFulfilled: (v) => {
                try {
                  this.parse(promise, onFulfilled(v), resolve, rejected);
                } catch (error) {
                  rejected(error);
                }
              },
              onRejected: (v) => {
                try {
                  this.parse(promise, onRejected(v), resolve, rejected);
                } catch (error) {
                  rejected(error);
                }
              },
            });
          }
          // 成功状态
          if (this.status === MP.FULFILED) {
            setTimeout(() => {
              try {
                this.parse(promise, onFulfilled(this.value), resolve, rejected);
              } catch (error) {
                rejected(error);
              }
            });
          }
          // 失败状态
          if (this.status === MP.REJECTED) {
            setTimeout(() => {
              try {
                this.parse(promise, onRejected(this.value), resolve, rejected);
              } catch (error) {
                rejected(error);
              }
            });
          }
        });
        return promise;
      }
      // 公共方法
      parse(promise, value, resolve, rejected) {
        if (promise === value) {
          throw new TypeError('Chaining cycle detected for promise');
        }
        try {
          const res = value;
          if (res instanceof MP) {
            res.then(resolve, rejected);
          } else {
            resolve(res);
          }
        } catch (error) {
          rejected(Error);
        }
      }
    
      static resolve(value) {
        return new MP((resolve, reject) => {
          if (value instanceof MP) {
            value.then(resolve, reject);
          } else {
            resolve(value);
          }
        });
      }
      static rejected(value) {
        return new MP((resolve, rejected) => {
          if (value instanceof MP) {
            value.then(resolve, rejected);
          } else {
            rejected(value);
          }
        });
      }
      static all(promises) {
        const values = [];
        let i = 0;
        return new MP((resolve, reject) => {
          promises.forEach((promise, index) => {
            promise.then(
              (value) => {
                i++;
                values[index] = value;
                if (i === promises.length) {
                  resolve(values);
                }
              },
              (reason) => {
                reject(reason);
              }
            );
          });
        });
      }
      static race(promises) {
        return new MP((resolve, reject) => {
          promises.forEach((promise) => {
            promise.then(
              (value) => {
                resolve(value);
              },
              (reason) => {
                reject(reason);
              }
            );
          });
        });
      }
    }
复制代码

参考链接

分类:
前端