七、js手写函数第七批


31、Promise手写

<script>
  const PENDING = 'pending';
  const FULFILLED = 'fulfilled';
  const REJECTED = 'rejected';

  class myPromise {
    constructor(executor) {
      this.status = PENDING;              // 初试状态为pending
      this.value = undefined;							// 存储成功的结果
      this.reason = undefined;            // 存储失败的原因
      this.onResolvedCallbacks = [];      // 成功的回到列表
      this.onRejectedCallbacks = [];			// 失败的回调列表

      let resolve = (value) => {
        if (this.status === PENDING) {
          this.status = FULFILLED;
          this.value = value;
          this.onResolvedCallbacks.forEach((fn) => fn());
        }
      };

      let reject = (reason) => {
        if (this.status === PENDING) {
          this.status = REJECTED;
          this.reason = reason;
          this.onRejectedCallbacks.forEach((fn) => fn());
        }
      };

      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    then(onFulfilled, onRejected) {
      // 解决 onFufilled,onRejected 没有传值的问题
      onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
      // 因为错误的值要让后面访问到,所以这里也要抛出错误,不然会在之后 then 的 resolve 中捕获
      onRejected = typeof onRejected === "function" ? onRejected : (err) => {
        throw err;
      };
      // 每次调用 then 都返回一个新的 promise
      let promise2 = new Promise((resolve, reject) => {
        if (this.status === FULFILLED) {
          //Promise/A+ 2.2.4 --- setTimeout
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              // x可能是一个proimise
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        }

        if (this.status === REJECTED) {
          //Promise/A+ 2.2.3
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        }

        if (this.status === PENDING) {
          this.onResolvedCallbacks.push(() => {
            setTimeout(() => {
              try {
                let x = onFulfilled(this.value);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
          });

          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              try {
                let x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
          });
        }
      });

      return promise2;
    }
  }

  const resolvePromise = (promise2, x, resolve, reject) => {
    // 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise  Promise/A+ 2.3.1
    if (promise2 === x) {
      return reject(
        new TypeError("Chaining cycle detected for promise #<Promise>"));
    }
    // Promise/A+ 2.3.3.3.3 只能调用一次
    let called;
    // 后续的条件要严格判断 保证代码能和别的库一起使用
    if ((typeof x === "object" && x != null) || typeof x === "function") {
      try {
        // 为了判断 resolve 过的就不用再 reject 了(比如 reject 和 resolve 同时调用的时候)  Promise/A+ 2.3.3.1
        let then = x.then;
        if (typeof then === "function") {
          // 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty  Promise/A+ 2.3.3.3
          then.call(
            x, (y) => {
              // 根据 promise 的状态决定是成功还是失败
              if (called) return;
              called = true;
              // 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
              resolvePromise(promise2, y, resolve, reject);
            }, (r) => {
              // 只要失败就失败 Promise/A+ 2.3.3.3.2
              if (called) return;
              called = true;
              reject(r);
            });
        } else {
          // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4
          resolve(x);
        }
      } catch (e) {
        // Promise/A+ 2.3.3.2
        if (called) return;
        called = true;
        reject(e);
      }
    } else {
      // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4
      resolve(x);
    }
  };
  debugger
  const promise = new myPromise((resolve, reject) => {
    resolve('true');
    reject('error')
  })

  promise.then(1);

  /* promise.then((val) => {
    console.log(val)
    resolve('newTrue')
  }).then((newVal) => {
    console.log(newVal)
  }).catch(err => {
    console.log(err);
  }) */
  debugger
</script>

32、filter函数

  <script>
    Array.prototype.myFilter = function (callback, thisArg) {
      if (this == null) {
        throw new TypeError('this is null or undefined')
      }
      if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function')
      }
      // 这里Object()传入一个数组会返回原数组,目的就是为了强转换,因为在严格模式下,有时候this并不会被直接当作对象用;
      // 这里之所以用Object只是把this转换一下,不然不好this.length;
      const O = Object(this);
      // 无符号右移零位:结果就是正数不变,零不变,小数的小数点后的位数会被舍弃,负数会完全乱掉
      // 需要注意的是数字字符串会被转换成number并取整,普通字符串直接转成零
      const len = O.length >>> 0;
      let k = 0, res = [];
      while (k < len) {
        if (k in O) {
          if (callback.call(thisArg, O[k], k, O)) {
            res.push(O[k]);
          }
        }
        k++;
      }
      return res;
    }
    const arr = [2, 4, 5];
    const newArr = arr.myFilter((item) => {
      return item !== 4;
    });
    console.log(newArr)


// 解释一下thisArg是干嘛的!!!!!!!!!
//在JavaScript中,thisArg参数在Array.prototype.filter方法中用来指定回调函数callback内this的值。如果不传递thisArg,那么this将被设置为undefined(严格模式),或者自动绑定到全局对象(在非严格模式下),这取决于运行环境和是否开启严格模式(use strict)。

// thisArg的意义主要体现在当你希望在回调函数中访问特定对象的属性或方法时,可以通过这个参数来指定这个特定的对象,避免使用全局变量或其他方法来访问这些数据。

// 下面是一个具体的例子来解释这个概念。

// 假设我们有一个对象,其中包含一些属性和一个方法,我们想要在过滤数组时,使用这个方法来检查数组元素是否符合某个条件:
var person = {
    minAge: 18,
    checkAge: function(age) {
        return age >= this.minAge;
    }
};
var ages = [16, 21, 15, 30, 18];
// 使用 filter 方法过滤 ages 数组
var adults = ages.filter(person.checkAge, person);  // 注意这里传入了 person 作为 thisArg
console.log(adults);  // [21, 30, 18]
// 在上面的示例中,person.checkAge函数中的this正常情况下如果不传值,会引用全局对象或者undefined(严格模式)。这个时候方法就不是person对象调用的,this就无法访问minAge。通过传入person作为thisArg,我们可以在checkAge方法中通过this.minAgen能够访问到person对象的minAge属性值。如果是undefined,调用函数的时候里面的this就是undefined,但是没关系,我们不用this也不会有事,正常的回调函数里使用不上this的。也就是说当你用filter函数的时候,如果你的第一个回调函数里用到了this,那你就应该传第二个参数,代表this的指向。除此之外第二个参数是没有必要传的

// 这样,callback函数内的this指向了person对象,确保了函数逻辑的正确执行。这也展示了thisArg如何使回调函数中的this关联到适当的上下文中,从而避免一些常见的错误,如全局变量污染或者更复杂的闭包实现。
  </script>

33、some函数

<script>
  Array.prototype.mySome = function(callback, thisArg) {
    if(this === null) {
      throw new TypeError('this is null or not defined')
    }
    if(typeof callback !== 'function') {
      throw new TypeError(callback + 'is not a function')
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0;
    while(k < len) {
      if(k in O) {
        if(callback.call(thisArg,O[k],k,O)){
          return true;
        }
      }
      k++;
    }
    return false;
  }
  const arr = [2,4,5];
  const newArr = arr.mySome((item) => {
    return item === 4;
  });
  console.log(newArr)
</script>

34、reduce函数

<script>
  Array.prototype.myReduce = function(callback, initValue) {
    if(this === null) {
      throw new TypeError('this is null or not defined')
    }
    if(typeof callback !== 'function') {
      throw new TypeError(callback + 'is not a function')
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0,acc;
    debugger
    if(arguments.length > 1) {
      acc = initValue
    } else {
      // 没传入初始值的时候,取数组中第一个非 empty 的值为初始值
      // 是不是觉着很神奇给你看一个数组 [ , 1, 2, 3]
      // 这个数组的第一位就是empty,这个数组是没有0索引的,你说神不神奇
      while(k < len && !(k in O)) {
        k++;
      }
      if(k > len) {
        throw new TypeError('Reduce of empty array with no initial value')
      }
      // 注意这里先执行赋值,再执行++运算
      acc = O[k++];
    }
    while(k < len) {
      if(k in O) {
        // 加完之后返回给acc,再循环继续加
        acc = callback(acc,O[k],k,O)
      }
      k++
    }
    return acc;
  }
  const arr = ['',4,5,3,6];
  const newArr = arr.myReduce((a,b) => {
    return a + b;
  });
  console.log(newArr)
</script>

35、map函数

<script>
  Array.prototype.myMap = function (callback, thisArg) {
    if (this === null) {
      throw new TypeError('this is null or not defined')
    }
    if (typeof callback !== 'function') {
      throw new TypeError(callback + 'is not a function')
    }
    debugger
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0, res = [];
    while (k < len) {
      // 如果指定的属性是否在指定的对象或其原型链中,在的话返回true
      // 这里O是一个数组,k是值,在O中有索引等于k就进来
      if (k in O) {
        res[k] = callback.call(thisArg, O[k], k, O);
      }
      k++;
    }
    return res;
  }
  const arr = [2, 4, 5];
  const newArr = arr.myMap((item) => {
    return item + 10;
  });
  console.log(newArr)
</script

文章作者: 吴俊杰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吴俊杰 !
  目录