反复刷这些javascript手写题,我又变强了

Posted 阿锋不知道丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反复刷这些javascript手写题,我又变强了相关的知识,希望对你有一定的参考价值。

数组原型方法的实现

其实下面的这几个方法的写法差不多,参数也只有一些细微差别,但是要考虑到,如果是要返回一个新的数组的方法比如map,reduce都要用到深拷贝

forEach

这里主要需要注意的就是第一个参数是回调函数
,然后这个回调 函数也会有三个参数,分别是元素值,元素索引,遍历数组本身,第二个参数就是数组遍历时this的指向地址,下面的filter,map都是这样的参数类型

Array.prototype._forEach = function (cb, ...ret) {
  if (typeof cb !== 'function') {
    console.log('first param must be function');
  }

  if (!Array.isArray(this)) {
    throw new Error('is not a  array');
  }
  // console.log(cb);
  // console.log(ret[0]);
  let arr = this;
  let len = arr.length;
  let arg = ret[0] || window;
//fo循环每次都可以调用这个回调函数的一些逻辑处理
  for (let i = 0; i < len; i++) {
    cb.call(arg, arr[i], i, arr);
  }
};

const arr = [1, 2, 3];
const obj = { a: 1 };
// arr.forEach(function (item, index, array) {
//   console.log(this); //可以传第二个参数,如果第二个参数是obj,那么this就是
//   // 指向的是obj
//   console.log(item, index, array);
// }, obj);

arr._forEach(function (item, index, arr) {
  console.log(item, index, arr);
}, obj);

在这里插入图片描述

map

这里我觉得唯独需要注意的就是,返回一个新的数组,需要用到深拷贝

深拷贝与浅拷贝的理解和实现

Array.prototype._map = function (cb, ret) {
  let arr = this;

  let res = [];
  if (!Array.isArray(this)) {
    throw new Error('is not a  array');
  }

  for (let i = 0; i < arr.length; i++) {
    // 如果数据里面有对象等引用类型,那么就需要执行深拷贝

    //res.push(cb.call(ret, deepClone(arr[i]), i, arr));
    res.push(cb.call(ret, arr[i], i, arr));
  }
  return res;
};

const arr = [1, 2, 3];
const obj = { a: 1 };

let newArr = arr._map(function (item, index, arr) {
  console.log(item, index, arr);
  return item * 2;
}, obj);
console.log(newArr);

console.log(arr);

在这里插入图片描述

filter

逻辑上的处理差不多和上面的

Array.prototype._filter = function (cb) {
  let arr = this;
  let arg = arguments[1];
  let res = [];

  if (!Array.isArray(arr)) {
    throw new Error('is not a array');
  }

  for (let i = 0; i < arr.length; i++) {
    let flag = cb.call(arg, arr[i], i, arr);
    if (flag) {
      res.push(arr[i]);
    }
  }

  return res;
};

const arr = [1, 2, 3];
let newArr = arr._filter(function (item) {
  return item > 1;
});
console.log(newArr);

在这里插入图片描述

every

会写这个了肯定会写some方法了

Array.prototype._every = function (cb) {
  let arr = this;
  let arg = arguments[1] || window;
  for (let i = 0; i < arr.length; i++) {
    let flag = cb.call(arg, arr[i], i, arr);
    if (!flag) {
      return false;
    }
  }

  return true;
};

const arr = [1, 2, 3];

let res = arr._every(function (item, index, array) {
  return item > 0;
});

console.log(res);


在这里插入图片描述

reduce

这个也是需要深拷贝的

Array.prototype._reduce = function (cb, initialValue) {
  let arr = this;
  let item;

  for (let i = 0; i < arr.length; i++) {
    // item = deepClone(arr[i]); 这里是需要深拷贝的
    initialValue = cb.call(arr, initialValue, arr[i], i, arr);
  }
  return initialValue;
};

const arr = [1, 3, 3];

let newArr = arr._reduce(function (pres, cur) {
  return pres + cur;
}, 0);

console.log(newArr);

在这里插入图片描述

对象原型方法的实现

object.assign

对于Object.assign需要注意的一点就是,合并对象的属性时,后面合并的有相同属性会覆盖掉前面合并的相同属性,而且切记,它只能合并可枚举的属性,也就是enumable为true的属性,思路很简单,由于object.assgin是浅拷贝只用简单的复制语句就能复制指针,于是第一层遍历合并的一些对象,然后第二层遍历对象的各个属性然后进行合并

// 浅拷贝
Object._assign = function (target, ...source) {
  if (target == null) {
    throw new TypeError('Cannot convert undefined or null to object');
  }
  // console.log(source);

  let ret = Object(target);
  source.forEach((obj) => {
    if (obj != null) {
      for (let key in obj) {
        console.log(key);
        // 可枚举的属性才会拷贝
        if (obj.hasOwnProperty(key)) {
          ret[key] = obj[key];
        }
      }
    }
  });
};

const a = {
  x: 1,
  y: {
    z: 1,
  },
};

const b = {
  x: 2,
  name: 'af',
  hobbies: {
    sports: 'basketball',
    music: {
      style: ['fuck', 'blues'],
      instruments: ['guitar'],
    },
  },
};

// Object.assign(a, b);
Object._assign(a, b);
console.log(a);


附加一到关于Object.assign的题

// 面试题
const v1 = 123;
const v2 = '123';
const v3 = true;
const v4 = function test() {};

const v5 = Object.assign({}, v1, v2, v3, v4);
console.log(v5);
// 打印结果 :{0: "1", 1: "2", 2: "3"}
// 结果很巧妙:这是因为Object.assign会自动将不是对象的方法转化为对象
// 然后只会合并可枚举的属性,而上面拥有可枚举的属性的对象就只有String对象

const r1 = new Number(v1);
const r2 = new String(v2);
const r3 = new Boolean(v3);
const r4 = new Function(v4);
console.log(r1);
console.log(r2);
console.log(r3);
console.log(r4);

for (let key in r1) {
  console.log(key);
}

for (let key in r2) {
  console.log(key); //唯独只有这里可以打印
}

for (let key in r3) {
  console.log(key);
}

for (let key in r4) {
  console.log(key);
}

object.create

用这个方法能够非常简单的把组合继承变为寄生组合继承,由于它是利用了父构造函数的原型对象,创建一个新的原型对象来使子构造函数的__proto__ 来指向实现继承,相对于组合继承少了一次访问构造函数的有点

第二个参数是设置对象自己的属性,而且必须是一个对象descripter描述器,所以直接可以用Object.definepropert实现

// 第二个参数是设置对象自己的属性,而且必须是一个对象descripter描述器
Object._create = function (proto, propertyObject = undefined) {
  if (typeof proto !== 'object' && typeof proto !== 'function') {
    throw new TypeError(
      'Object prototype may only be Object or null to Object',
    );
  }
  if (propertyObject == null) {
    new TypeError('cannot convert undefined or null to Object');
  }

  // 关键就这三行
  function F() {}
  F.prototype = proto;
  const obj = new F();

  if (propertyObject !== undefined) {
    Object.defineProperty(obj, propertyObject);
  }

  if (proto === null) {
    // 创建一个没有原型的对象 Object.create(null)
    obj._proto_ = null;
  }

  return obj;
};

手写promise

這個相比不用多说有多重要了,天天用到的,所以在学习实现手写之前必须掌握好的它的一些特点和细节。

由于细节有点多,先写个只完成基本功能的,还有一些promise方法,没写我准备会在b站上发布一个视频来解析我的代码并贴在这里,如果一直没发布希望有人评论提醒我

下面代码有详细注释

class Af {
  // 先定义三种状态
  static PENGDING = 'pengding';
  static FULLFILLED = 'fullfilled';
  static REJECTED = 'rejected';

  constructor(executor) {
    this.status = Af.PENGDING;
    this.value = '';

    // 把状态改变后的函数放到下面数组中执行
    // 等状态改变之后再拿出来执行

    this.callbacks = [];
    // 因为resolve和reject方法是在executor中调用,作用域也是executor的作用域
    // 这样会造成this指向window,现在我么使用的是class定义,this会是undefined
    // 所以下面绑定了this
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      // 当执行者出现异常时触发拒绝状态
      this.reject(error);
    }
  }

  resolve(value) {
    // 状态只能改变一次所以在resolve和reject都需要添加状态条件判断
    // 而且之能是 pending->fullfilled  pending->rejected
    if (this.status == Af.PENGDING) {
      this.status = Af.FULLFILLED;
      this.value = value;

      // 添加处理callback方法的代码 异步处理s
      setTimeout(() => {
        this.callbacks.forEach((callback) => callback.onFullfilled(value));
      });
    }
  }

  reject(value) {
    // 状态只能改变一次所以在resolve和reject都需要添加状态条件判断
    // 而且之能是 pending->fullfilled  pending->rejected
    if (this.status == Af.PENGDING) {
      this.status = Af.REJECTED;
      this.value = value;

      // 添加处理callback方法的代码 异步处理
      setTimeout(() => {
        this.callbacks.forEach((callback) => callback.onRejected(value));
      });
    }
  }

  // onFullfilled,onRjected即成功和错误时的回调函数
  then(onFullfilled, onRejected) {
    // then可以有两个参数,即成功和错误时的回调函数
    // then的函数参数都不是必须的,所以需要设置默认为函数
    // 用于处理当没有传递时的情况
    if (typeof onFullfilled !== 'function') {
      onFullfilled = (value) => value;
    }

    if (typeof onRejected !== 'function') {
      onRejected = (value) => value;
    }

    // 返回一个新的promise对象 ,使.then能够链式调用
    return new Af((resolve, reject) => {
      // .then处理pengding状态(如果rejected被异步回调函数包裹,比如用setTimeOut包裹的
      //   时候,状态是异步被改变的,那么没有下面Pending状态的处理,将不会执行.then方法,因为
      //   定时函数之前一直没有执行resolve方法)
      if (this.status == Af.PENGDING) {
        // 将then方法的回调函数添加到 callbacks 数组中,用于异步执行
        this.callbacks.push({
          onFullfilled: (value) => {
            try {
              let res = onFullfilled(value);
              resolve(res);
            } catch (error) {
              reject(error);
            }
          },
          onRejected: (value) => {
            try {
              let res = onRejected(value);
              reject(res);
            } catch (error) {
              reject(error);
            }
          },
        });
      }

      // .then处理fullfilled状态
      if (this.status == Af.FULLFILLED) {
        // setTimeOut的作用是使.then回调函数其异步执行
        setTimeout(() => {
          try {
            let res = onFullfilled(this.value);
            resolve(res);
          } catch (error) {
            reject(error);
          }
        });
      }

      // .then处理rejected状态
      if (this.status == Af.REJECTED) {
        setTimeout(() => {
          try {
            let res = onRejected(this.value);
            reject(res);
          } catch (error) 反复刷这些javascript手写题,我又变强了

反复刷这些javascript面试手写题,我感觉我真的变强了

Android程序员(三~五年)跳槽涨薪必刷面试题有备无患查漏补缺

15 道二叉树手写算法题

知乎万赞,值得反复刷的Android面试题

腾讯T8纯手写66个微服务架构设计模式,全部学会真的“变强”了