阅读 360

JS 基础--手写专栏

本文记录一些 js 一些基础手写题

手写 call

思路:(apply 实现思路类似,参数处理不同)
1: 第一个参数为undefinednull的时候,那么会转变为window
2:改变了this执行,让新的对象可以执行该函数

Function.prototype.myCall = function (context) {
    // 如果不传的话默认上下文是window
    context = context || window
    // 给content创建一个fn属性,并将值设置为需要调用的函数 this 指需要调用的函数
    context.fn = this
    // 取出参数
    const args = [...arguments].slice(1)
    const result = context.fn(...args)
    // 删除多余函数
    delete context.fn
    return result
}
复制代码

手写 bind

思路:
bind返回一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过new的方式
(1) 直接调用,参数需要注意以下情况:因为bind可以实现类似这样的代码 f.bind(obj,1)(2)
(2) new的方式, 不会被任何方式改变this

Function.prototype.myBind = function(context) {
  if (typeof this !== 'function') {
      throw new TypeError('Error')
  }
  //返回一个绑定this的函数,这里我们需要保存this
  const _this = this
  // 取出参数
  const args = [...arguments].slice(1)
  //返回一个函数
  return function F() {
      //因为返回一个函数,我们可以new F()需要判断能当做构造函数吗
      if (this instanceof F) {
          return new _this(...args, ...arguments)
      }
      // 直接调用,
      return _this.apply(context, args.concat(...arguments))
  }
}

复制代码

手写 New

思路:
(1)创建一个空对象
(2)将新对象挂载到原型链上
(3)改this指向
(4)如果构造函数返回一个对象,则将该对象返回,否则返回步骤1创建的对象

function myNew(obj, ...args) {
  const newObj = Object.create(obj.prototype);//Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
  const result = obj.apply(newObj, args);
  return (typeof result === 'object' && result !== null) ? result : newObj;
}
复制代码

手写 instanceof

思路:
根据原型链进行查找判断 leftValue 是否为 rightValue 的实例,思想是在 leftValue 的原型链上,即 leftValue.__proto__ 上寻找是否存在 rightValue.prototype

function newInstanceOf (leftValue, rightValue) {
    if (typeof leftValue !== 'object' || rightValue == null) { 
        return false;
    }
    
    let rightProto = rightValue.prototype;
    leftValue = leftValue.__proto__;
    
    while (true) {
        if (leftValue === null) return false;
        if (leftValue === rightProto) return true;
        leftValue = leftValue.__proto__;
    }
}
复制代码

手写 Promise/A+规范

思路:
(1)可以接收一个 executor 作为入参
(2)具备 pendingresolvedrejected 这三种状态
(2)then 链式调用,所以需要维护队列(包括resolvereject的队列)
(2)批量执行这个动作包装成异步任务,确保它一定可以在同步代码之后执行

function CutePromise(executor) {
    // value 记录异步任务成功的执行结果
    this.value = null;
    // reason 记录异步任务失败的原因
    this.reason = null;
    // status 记录当前状态,初始化是 pending
    this.status = 'pending';
  
    // 缓存两个队列,维护 resolved 和 rejected 各自对应的处理函数
    this.onResolvedQueue = [];
    this.onRejectedQueue = [];
         
    // 把 this 存下来,后面会用到
    var self = this;
  
    // 定义 resolve 函数
    function resolve(value) {
        // 如果不是 pending 状态,直接返回
        if (self.status !== 'pending') {
            return;
        }
        // 异步任务成功,把结果赋值给 value
        self.value = value;
        // 当前状态切换为 resolved
        self.status = 'resolved'; 
        // 用 setTimeout 延迟队列任务的执行
        setTimeout(function(){
            // 批量执行 resolved 队列里的任务
            self.onResolvedQueue.forEach(resolved => resolved(self.value)); 
        });
    }
        
    // 定义 reject 函数
    function reject(reason) {
        // 如果不是 pending 状态,直接返回
        if (self.status !== 'pending') {
            return;
        }
        // 异步任务失败,把结果赋值给 value
        self.reason = reason; 
        // 当前状态切换为 rejected
        self.status = 'rejected';
        // 用 setTimeout 延迟队列任务的执行
        setTimeout(function(){
            // 批量执行 rejected 队列里的任务
            self.onRejectedQueue.forEach(rejected => rejected(self.reason));
        });
    }
  
    // 把 resolve 和 reject 能力赋予执行器
    executor(resolve, reject);
}

// then 方法接收两个函数作为入参(可选)
CutePromise.prototype.then = function(onResolved, onRejected) {
  
    // 注意,onResolved 和 onRejected必须是函数;如果不是,我们此处用一个透传来兜底
    if (typeof onResolved !== 'function') {
        onResolved = function(x) {return x};
    }
    if (typeof onRejected !== 'function') {
        onRejected = function(e) {throw e};
    }
 
    // 依然是保存 this
    var self = this;
    // 判断是否是 resolved 状态
    if (self.status === 'resolved') {
        // 如果是 执行对应的处理方法
        onResolved(self.value);
    } else if (self.status === 'rejected') {
        // 若是 rejected 状态,则执行 rejected 对应方法
        onRejected(self.reason);
    } else if (self.status === 'pending') {
        // 若是 pending 状态,则只对任务做入队处理
        self.onResolvedQueue.push(onResolved);
        self.onRejectedQueue.push(onRejected);
    }
    return this
};
复制代码

最后

个人感觉类似手写的问题很容易忘记,需要注意的是理解其中的原理,然后再根据原理反推源码,这个比死记硬背代码(不会真有人能背下来吧)轻松多了。继续加油

image.png

0050C907.png

0050C907.png

文章分类
前端
文章标签