JavaScript 系列 - Function.prototype.apply

112 阅读2分钟

概念

Function.prototype.apply(thisArg [, argsArray])

apply() 方法调用时,改变函数内部 this 指向,传入一个数组或一个类数组对象的形式提供的参数,函数会把数组进行解构传递。

  • thisArg

    • 在 func 函数运行时使用的 this 值。
    • 请注意,this 可能不是该方法看到的实际值:如果这个函数处于非严格模式下,null 或 undefined 自动替换为指向全局对象,原始值会被包装
    • 当第一个参数为 nullundefined 时,可以使用数组展开语法实现类似的结果
  • argsArray 可选

    一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。

使用示例

用 apply 将数组各项添加到另一个数组

const array = ["a", "b"];
const elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]

使用 apply 和内置函数

  • 对于一些需要写循环以遍历数组各项的需求,我们可以用 apply 完成以避免循环
// 找出数组中最大/小的数字
const numbers = [5, 6, 2, 3, 7];

// 使用 Math.min/Math.max 以及 apply 函数时的代码
let max = Math.max.apply(null, numbers); // 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..)
let min = Math.min.apply(null, numbers);
  • 数组切块
function minOfArray(arr) {
  let min = Infinity;
  let QUANTUM = 32768;

  for (let i = 0, len = arr.length; i < len; i += QUANTUM) {
    const submin = Math.min.apply(
      null,
      arr.slice(i, Math.min(i + QUANTUM, len)),
    );
    min = Math.min(submin, min);
  }

  return min;
}

使用 apply 来链接构造器

构造器中使用一个类数组对象而非参数列表

Function.prototype.construct = function (aArgs) {
  let oNew = Object.create(this.prototype);
  this.apply(oNew, aArgs);
  return oNew;
};
function MyConstructor() {
  for (let nProp = 0; nProp < arguments.length; nProp++) {
    this["property" + nProp] = arguments[nProp];
  }
}

let myArray = [4, "Hello world!", false];
let myInstance = MyConstructor.construct(myArray);

console.log(myInstance.property1); // logs 'Hello world!'
console.log(myInstance instanceof MyConstructor); // logs 'true'
console.log(myInstance.constructor); // logs 'MyConstructor'

模拟

Function.prototype.newApply = function (context, parameter) {
  if (typeof this !== "function") {
    console.error("type error");
  }
  if (typeof context === "object") {
    context = context || window;
  } else {
    context = Object.create(null);
  }
  if (!(parameter instanceof Array)) {
    parameter = [];
  }
  context.fn = this;
  var args = [],
    result = null;
  for (var i = 0, len = parameter.length; i < len; i++) {
    args.push("parameter[" + i + "]");
  }
  result = eval("context.fn(" + args + ")");
  delete context.fn;
  return result;
};

Function.prototype.newApply = function (context, parameter) {
  if (typeof this !== "function") {
    console.error("type error");
  }
  if (typeof context === "object") {
    context = context || window;
  } else {
    context = Object.create(null);
  }
  if (!Array.isArray(parameter)) {
    parameter = [];
  }
  let fn = Symbol();
  let result = null;
  context[fn] = this;
  result = context[fn](...parameter);
  delete context[fn];
  return result;
};