Skip to content
本页目录

无敌手写秘籍

1、New

首先要知道 new 到底做了什么:

  • (1) 在内存中创建一个新对象。
  • (2) 这个新对象内部的 [[Prototype]] 特性被赋值为构造函数的 prototype 属性。
  • (3) 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。
  • (4) 执行构造函数内部的代码(给新对象添加属性)。
  • (5) 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
js
/**
 *
 * @param {Function} func 构造函数
 * @param  {...any} args
 */
const myNew = function (func, ...args) {
  if (typeof func !== 'function') {
    return new TypeError('this is not a function');
  }
  // 创建一个空对象,指定原型为 func.prototype
  const obj = Object.create(func.prototype);
  // 绑定 this 并执行构造函数内部代码给新对象加上属性
  const result = func.apply(obj, args);
  // 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
  return result && result instanceof Object ? result : obj;
};
// 测试
// function Person(name, age) {
// 	this.name = name;
// 	this.age = age;
// }

// 用来测试返回引用数据类型
function Person(name, age) {
  this.name = name;
  this.age = age;
  return function () {
    console.log('返回引用数据类型');
  };
}

console.log('myNew');
const jing = myNew(Person, 'jing', 22);
console.log(jing);
jing();
console.log('new');
const hao = new Person('hao', 23);
console.log(hao);
hao();

2、instanceof

js
// 基于原型链一直向上查找,知道达到 object 还未找到返回 false

const myInstanceof = (left, right) => {
  while (left) {
    if (left === right.prototype) {
      return true;
    }
    left = left.__proto__;
  }
  return false;
};

console.log(myInstanceof(2, Number)); // true
console.log(myInstanceof([], Number)); // false
console.log(myInstanceof([], Object)); // true

3、call、apply

js
Function.prototype.myCall = function (thisArg, ...args) {
  if (!thisArg) {
    // thisArg 为 null 或者是 undefined
    thisArg = typeof window === 'undefined' ? global : window;
  }
  // 保存this
  thisArg.fn = this;
  // 执行代码
  let res = thisArg.fn(...args); //重点代码,利用this指向,相当于context.caller(...args)
  // 删除变量 释放空间
  delete thisArg.fn;
  // 返回结果
  return res;
};

var foo = {
  name: 'Selina'
};
var name = 'Chirs';
function bar(job, age) {
  console.log(this.name);
  console.log(job, age);
}
bar.myCall(foo, 'programmer', 20);
// Selina programmer 20
bar.myCall(null, 'teacher', 25);
// 浏览器环境: Chirs teacher 25; node 环境: undefined teacher 25

Function.prototype.myApply = function (thisArg, args) {
  if (!thisArg) {
    // thisArg 为 null 或者是 undefined
    thisArg = typeof window === 'undefined' ? global : window;
  }
  // 保存this
  thisArg.fn = this;
  // 执行代码
  let res = thisArg.fn(...args); //重点代码,利用this指向,相当于context.caller(...args)
  // 删除变量 释放空间
  delete thisArg.fn;
  // 返回结果
  return res;
};

const numbers = [5, 6, 2, 3, 7];

// Function.prototype.myApply()
console.log('Function.prototype.myApply()');

const max = Math.max.myApply(null, numbers);
console.log(max); // 7

// Function.prototype.apply()
console.log('Function.prototype.apply()');
const min = Math.min.apply(null, numbers);
console.log(min); // 2

4、bind

js
Function.prototype.myBind = function (thisArg) {
  // 设置 fn 为调用 myBind 的函数
  const fn = this;
  // 得到调用 myBind 函数时传递的参数
  const otherArgs = [...arguments].slice(1);
  // 定义一个返回的函数
  const result = function () {
    // 得到执行该函数时的参数
    const resultArgs = [...arguments];
    const isNew = this instanceof result;
    return fn.apply(isNew ? this : thisArg, otherArgs.concat(resultArgs));
  };
  // 绑定原型,保证原函数的原型对象的属性不丢失,result 是调用 myBind 要返回的函数,下面的 this 是调用 myBind 的函数。把 this 上的原型绑定到返回的函数上面。
  result.prototype = this.prototype;
  // 返回结果
  return result;
};

// 测试代码
function Animal(name, color) {
  this.name = name;
  this.color = color;
}
Animal.prototype.say = function () {
  return `I'm a ${this.color} ${this.name}`;
};
const Cat = Animal.myBind(null, 'cat');
const cat = new Cat('white');
console.log(cat, cat.say());
if (cat.say() === "I'm a white cat" && cat instanceof Cat && cat instanceof Animal) {
  console.log('success');
}

// Animal { name: 'cat', color: 'white' } I'm a white cat
// success

5、函数柯里化

js
// 柯里化求和函数
var add = function () {
  var _args = [];
  return function () {
    if (arguments.length === 0) {
      return _args.reduce(function (a, b) {
        return a + b;
      });
    }
    [].push.apply(_args, arguments);
    return arguments.callee;
  };
};
var sum = add();
sum(100, 200)(300);
sum(400);
console.log(sum(500)(5000)()); // 1000

//通用柯里化函数

var curry = function (fn) {
  var len = fn.length,
    args = [];
  return function () {
    Array.prototype.push.apply(args, arguments);
    var argsLen = args.length;
    if (argsLen < len) {
      return arguments.callee;
    }
    return fn.apply(fn, args);
  };
};
var add = function (a, b, c) {
  return a + b + c;
};

var mult = function (a, b, c) {
  return a * b * c;
};

var adder = curry(add);
console.log(adder(1)(2)(3));
var multer = curry(mult);
console.log(multer(2)(5)(10));

6、防抖节流

js
/**
 * 防抖
 * @param {Function} fn
 * @param {Number} time
 */
function debounce(fn, time) {
  let timeout = null;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      fn.apply(this, args);
    }, time);
  };
}

/**
 * 节流
 * @param {*} fn
 * @param {*} delay
 */
function throttle(fn, delay) {
  let prevTime = Date.now();
  return (...args) => {
    let curTime = Date.now();
    if (curTime - prevTime > delay) {
      fn.apply(this, args);
      prevTime = curTime;
    }
  };
}

7、 promise.all

js
/**
 * 1、判断入参是否合法
 * 2. 函数返回值为 `<Promise>` 对象, 当参数 `promises` 内所有的 `Promise` 成功,
 *    该 `<Promise>` 对象成功, 成功,数据为所有成功的 `Promise` 结果数组。
 *    有一个不成功, 则该 `<Promise>` 不成功, 失败数据为失败原因
 */
Promise._all = (promises) => {
  // 该方法返回一个promise对象
  return new Promise((resolve, reject) => {
    // 该方法的参数需为一个可迭代对象
    if (promises === null || typeof promises[Symbol.iterator] !== 'function') {
      throw new TypeError(`${promises} is not a iterable`);
    }

    promises = [...promises];
    // 可迭代对象为空则 resolve 一个 []
    if (promises.length === 0) {
      resolve([]);
    }

    let result = [];
    let count = 0;
    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then((res) => {
          result[index] = res;
          count++;
          if (count === promises.length) {
            resolve(result);
          }
        })
        .catch((err) => {
          reject(err);
        });
    });
  });
};

// 验证
function testPromise() {
  try {
    Promise._all(null).then(
      (res) => console.log(res),
      (rej) => console.log(rej)
    );
    // throw err: null is not iterable
  } catch (e) {
    console.log(e);
  }
  try {
    Promise._all({}).then(
      (res) => console.log(res),
      (rej) => console.log(rej)
    );
    // throw err: [object object] is not iterable
  } catch (e) {
    console.log(e);
  }
  Promise._all([]).then(
    (res) => console.log(res),
    (rej) => console.log(rej)
  );
  // []
  Promise._all(new Set()).then(
    (res) => console.log(res),
    (rej) => console.log(rej)
  );
  // []
  Promise._all(new Map()).then(
    (res) => console.log(res),
    (rej) => console.log(rej)
  );
  // []
  Promise._all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3), 4]).then(
    (res) => console.log(res),
    (rej) => console.log(rej)
  );
  // [1, 2, 3, 4]
  Promise._all([Promise.reject(1), Promise.resolve(2), Promise.resolve(3), 4]).then(
    (res) => console.log(res),
    (rej) => console.log(rej)
  );
  // 1
}
testPromise();

MIT Licensed